1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 ci et: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/MathAlgorithms.h"
11 #include "AppWindow.h"
15 #include "nsPrintfCString.h"
17 #include "nsWidgetsCID.h"
18 #include "nsThreadUtils.h"
20 #include "nsQueryObject.h"
21 #include "mozilla/ProfilerLabels.h"
22 #include "mozilla/Sprintf.h"
23 #include "mozilla/Try.h"
25 // Interfaces needed to be included
26 #include "nsGlobalWindowOuter.h"
27 #include "nsIAppShell.h"
28 #include "nsIAppShellService.h"
29 #include "nsIDocumentViewer.h"
30 #include "mozilla/dom/Document.h"
31 #include "mozilla/dom/CanonicalBrowsingContext.h"
32 #include "nsPIDOMWindow.h"
34 #include "nsIInterfaceRequestor.h"
35 #include "nsIInterfaceRequestorUtils.h"
36 #include "nsIIOService.h"
37 #include "nsIObserverService.h"
38 #include "nsIOpenWindowInfo.h"
39 #include "nsIWindowMediator.h"
40 #include "nsIScreenManager.h"
41 #include "nsIScreen.h"
42 #include "nsIWindowWatcher.h"
44 #include "nsAppShellCID.h"
45 #include "nsReadableUtils.h"
46 #include "nsStyleConsts.h"
47 #include "nsPresContext.h"
48 #include "nsContentUtils.h"
49 #include "nsXULTooltipListener.h"
50 #include "nsXULPopupManager.h"
51 #include "nsFocusManager.h"
52 #include "nsContentList.h"
53 #include "nsIDOMWindowUtils.h"
54 #include "nsServiceManagerUtils.h"
57 #include "mozilla/AppShutdown.h"
58 #include "mozilla/AutoRestore.h"
59 #include "mozilla/Preferences.h"
60 #include "mozilla/PresShell.h"
61 #include "mozilla/Services.h"
62 #include "mozilla/SpinEventLoopUntil.h"
63 #include "mozilla/dom/BarProps.h"
64 #include "mozilla/dom/DOMRect.h"
65 #include "mozilla/dom/Element.h"
66 #include "mozilla/dom/Event.h"
67 #include "mozilla/dom/ScriptSettings.h"
68 #include "mozilla/dom/BrowserHost.h"
69 #include "mozilla/dom/BrowserParent.h"
70 #include "mozilla/dom/LoadURIOptionsBinding.h"
71 #include "mozilla/intl/LocaleService.h"
72 #include "mozilla/EventDispatcher.h"
75 # include "mozilla/PreXULSkeletonUI.h"
76 # include "nsIWindowsUIUtils.h"
79 #include "mozilla/dom/DocumentL10n.h"
81 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
82 # include "mozilla/widget/NativeMenuSupport.h"
83 # define USE_NATIVE_MENUS
86 #define SIZEMODE_NORMAL u"normal"_ns
87 #define SIZEMODE_MAXIMIZED u"maximized"_ns
88 #define SIZEMODE_MINIMIZED u"minimized"_ns
89 #define SIZEMODE_FULLSCREEN u"fullscreen"_ns
91 #define SIZE_PERSISTENCE_TIMEOUT 500 // msec
93 //*****************************************************************************
94 //*** AppWindow: Object Management
95 //*****************************************************************************
99 using dom::AutoNoJSAPI
;
100 using dom::BrowserHost
;
101 using dom::BrowsingContext
;
103 using dom::DocumentL10n
;
105 using dom::EventTarget
;
106 using dom::LoadURIOptions
;
109 AppWindow::AppWindow(uint32_t aChromeFlags
)
110 : mChromeTreeOwner(nullptr),
111 mContentTreeOwner(nullptr),
112 mPrimaryContentTreeOwner(nullptr),
114 mFullscreenChangeState(FullscreenChangeState::NotChanging
),
115 mContinueModalLoop(false),
117 mChromeLoaded(false),
118 mSizingShellFromXUL(false),
119 mShowAfterLoad(false),
120 mIntrinsicallySized(false),
121 mCenterAfterLoad(false),
122 mIsHiddenWindow(false),
123 mLockedUntilChromeLoad(false),
124 mIgnoreXULSize(false),
125 mIgnoreXULPosition(false),
126 mChromeFlagsFrozen(false),
127 mIgnoreXULSizeMode(false),
130 mDominantClientSize(false),
131 mChromeFlags(aChromeFlags
),
132 mWidgetListenerDelegate(this) {}
134 AppWindow::~AppWindow() {
142 //*****************************************************************************
143 // AppWindow::nsISupports
144 //*****************************************************************************
146 NS_IMPL_ADDREF(AppWindow
)
147 NS_IMPL_RELEASE(AppWindow
)
149 NS_INTERFACE_MAP_BEGIN(AppWindow
)
150 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIAppWindow
)
151 NS_INTERFACE_MAP_ENTRY(nsIAppWindow
)
152 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow
)
153 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor
)
154 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
155 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener
)
156 NS_INTERFACE_MAP_ENTRY_CONCRETE(AppWindow
)
159 nsresult
AppWindow::Initialize(nsIAppWindow
* aParent
, nsIAppWindow
* aOpener
,
160 int32_t aInitialWidth
, int32_t aInitialHeight
,
161 bool aIsHiddenWindow
,
162 widget::InitData
& widgetInitData
) {
164 nsCOMPtr
<nsIWidget
> parentWidget
;
166 mIsHiddenWindow
= aIsHiddenWindow
;
168 DesktopIntPoint initialPos
;
169 nsCOMPtr
<nsIBaseWindow
> base(do_QueryInterface(aOpener
));
171 LayoutDeviceIntRect rect
= base
->GetPositionAndSize();
173 DesktopIntRect::Round(rect
/ base
->DevicePixelsPerDesktopPixel());
174 if (!mOpenerScreenRect
.IsEmpty()) {
175 initialPos
= mOpenerScreenRect
.TopLeft();
176 ConstrainToOpenerScreen(&initialPos
.x
.value
, &initialPos
.y
.value
);
180 // XXX: need to get the default window size from prefs...
181 // Doesn't come from prefs... will come from CSS/XUL/RDF
182 DesktopIntRect
deskRect(initialPos
,
183 DesktopIntSize(aInitialWidth
, aInitialHeight
));
185 // Create top level window
186 if (gfxPlatform::IsHeadless()) {
187 mWindow
= nsIWidget::CreateHeadlessWidget();
189 mWindow
= nsIWidget::CreateTopLevelWindow();
192 return NS_ERROR_FAILURE
;
195 /* This next bit is troublesome. We carry two different versions of a pointer
196 to our parent window. One is the parent window's widget, which is passed
197 to our own widget. The other is a weak reference we keep here to our
198 parent AppWindow. The former is useful to the widget, and we can't
199 trust its treatment of the parent reference because they're platform-
200 specific. The latter is useful to this class.
201 A better implementation would be one in which the parent keeps strong
202 references to its children and closes them before it allows itself
203 to be closed. This would mimic the behaviour of OSes that support
204 top-level child windows in OSes that do not. Later.
206 nsCOMPtr
<nsIBaseWindow
> parentAsWin(do_QueryInterface(aParent
));
208 parentAsWin
->GetMainWidget(getter_AddRefs(parentWidget
));
209 mParentWindow
= do_GetWeakReference(aParent
);
212 mWindow
->SetWidgetListener(&mWidgetListenerDelegate
);
213 rv
= mWindow
->Create((nsIWidget
*)parentWidget
, // Parent nsIWidget
214 nullptr, // Native parent widget
215 deskRect
, // Widget dimensions
216 &widgetInitData
); // Widget initialization data
217 NS_ENSURE_SUCCESS(rv
, rv
);
219 LayoutDeviceIntRect r
= mWindow
->GetClientBounds();
220 // Match the default background color of content. Important on windows
221 // since we no longer use content child widgets.
222 mWindow
->SetBackgroundColor(NS_RGB(255, 255, 255));
224 // All Chrome BCs exist within the same BrowsingContextGroup, so we don't need
225 // to pass in the opener window here. The opener is set later, if needed, by
227 RefPtr
<BrowsingContext
> browsingContext
=
228 BrowsingContext::CreateIndependent(BrowsingContext::Type::Chrome
);
231 mDocShell
= nsDocShell::Create(browsingContext
);
232 NS_ENSURE_TRUE(mDocShell
, NS_ERROR_FAILURE
);
234 // Make sure to set the item type on the docshell _before_ calling
235 // InitWindow() so it knows what type it is.
236 NS_ENSURE_SUCCESS(EnsureChromeTreeOwner(), NS_ERROR_FAILURE
);
238 mDocShell
->SetTreeOwner(mChromeTreeOwner
);
241 NS_ENSURE_SUCCESS(mDocShell
->InitWindow(nullptr, mWindow
, r
.X(), r
.Y(),
242 r
.Width(), r
.Height()),
245 // Attach a WebProgress listener.during initialization...
246 mDocShell
->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK
);
248 mWindow
->MaybeDispatchInitialFocusEvent();
253 //*****************************************************************************
254 // AppWindow::nsIIntefaceRequestor
255 //*****************************************************************************
257 NS_IMETHODIMP
AppWindow::GetInterface(const nsIID
& aIID
, void** aSink
) {
260 NS_ENSURE_ARG_POINTER(aSink
);
262 if (aIID
.Equals(NS_GET_IID(nsIPrompt
))) {
263 rv
= EnsurePrompter();
264 if (NS_FAILED(rv
)) return rv
;
265 return mPrompter
->QueryInterface(aIID
, aSink
);
267 if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
))) {
268 rv
= EnsureAuthPrompter();
269 if (NS_FAILED(rv
)) return rv
;
270 return mAuthPrompter
->QueryInterface(aIID
, aSink
);
272 if (aIID
.Equals(NS_GET_IID(mozIDOMWindowProxy
))) {
273 return GetWindowDOMWindow(reinterpret_cast<mozIDOMWindowProxy
**>(aSink
));
275 if (aIID
.Equals(NS_GET_IID(nsIDOMWindow
))) {
276 nsCOMPtr
<mozIDOMWindowProxy
> window
= nullptr;
277 rv
= GetWindowDOMWindow(getter_AddRefs(window
));
278 nsCOMPtr
<nsIDOMWindow
> domWindow
= do_QueryInterface(window
);
279 domWindow
.forget(aSink
);
282 if (aIID
.Equals(NS_GET_IID(nsIWebBrowserChrome
)) &&
283 NS_SUCCEEDED(EnsureContentTreeOwner()) &&
284 NS_SUCCEEDED(mContentTreeOwner
->QueryInterface(aIID
, aSink
))) {
288 return QueryInterface(aIID
, aSink
);
291 //*****************************************************************************
292 // AppWindow::nsIAppWindow
293 //*****************************************************************************
295 NS_IMETHODIMP
AppWindow::GetDocShell(nsIDocShell
** aDocShell
) {
296 NS_ENSURE_ARG_POINTER(aDocShell
);
298 *aDocShell
= mDocShell
;
299 NS_IF_ADDREF(*aDocShell
);
303 NS_IMETHODIMP
AppWindow::GetZLevel(uint32_t* outLevel
) {
304 nsCOMPtr
<nsIWindowMediator
> mediator(
305 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
307 mediator
->GetZLevel(this, outLevel
);
313 NS_IMETHODIMP
AppWindow::SetZLevel(uint32_t aLevel
) {
314 nsCOMPtr
<nsIWindowMediator
> mediator(
315 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
316 if (!mediator
) return NS_ERROR_FAILURE
;
319 mediator
->GetZLevel(this, &zLevel
);
320 if (zLevel
== aLevel
) return NS_OK
;
322 /* refuse to raise a maximized window above the normal browser level,
323 for fear it could hide newly opened browser windows */
324 if (aLevel
> nsIAppWindow::normalZ
&& mWindow
) {
325 nsSizeMode sizeMode
= mWindow
->SizeMode();
326 if (sizeMode
== nsSizeMode_Maximized
|| sizeMode
== nsSizeMode_Fullscreen
) {
327 return NS_ERROR_FAILURE
;
332 mediator
->SetZLevel(this, aLevel
);
333 PersistentAttributesDirty(PersistentAttribute::Misc
, Sync
);
335 nsCOMPtr
<nsIDocumentViewer
> viewer
;
336 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
338 RefPtr
<dom::Document
> doc
= viewer
->GetDocument();
341 RefPtr
<dom::Event
> event
=
342 doc
->CreateEvent(u
"Events"_ns
, dom::CallerType::System
, rv
);
344 event
->InitEvent(u
"windowZLevel"_ns
, true, false);
346 event
->SetTrusted(true);
348 doc
->DispatchEvent(*event
);
355 NS_IMETHODIMP
AppWindow::GetChromeFlags(uint32_t* aChromeFlags
) {
356 NS_ENSURE_ARG_POINTER(aChromeFlags
);
357 *aChromeFlags
= mChromeFlags
;
361 NS_IMETHODIMP
AppWindow::SetChromeFlags(uint32_t aChromeFlags
) {
362 NS_ASSERTION(!mChromeFlagsFrozen
,
363 "SetChromeFlags() after AssumeChromeFlagsAreFrozen()!");
365 mChromeFlags
= aChromeFlags
;
372 NS_IMETHODIMP
AppWindow::AssumeChromeFlagsAreFrozen() {
373 mChromeFlagsFrozen
= true;
377 NS_IMETHODIMP
AppWindow::SetIntrinsicallySized(bool aIntrinsicallySized
) {
378 mIntrinsicallySized
= aIntrinsicallySized
;
382 NS_IMETHODIMP
AppWindow::GetIntrinsicallySized(bool* aIntrinsicallySized
) {
383 NS_ENSURE_ARG_POINTER(aIntrinsicallySized
);
385 *aIntrinsicallySized
= mIntrinsicallySized
;
389 NS_IMETHODIMP
AppWindow::GetPrimaryContentShell(
390 nsIDocShellTreeItem
** aDocShellTreeItem
) {
391 NS_ENSURE_ARG_POINTER(aDocShellTreeItem
);
392 NS_IF_ADDREF(*aDocShellTreeItem
= mPrimaryContentShell
);
397 AppWindow::RemoteTabAdded(nsIRemoteTab
* aTab
, bool aPrimary
) {
399 mPrimaryBrowserParent
= aTab
;
400 mPrimaryContentShell
= nullptr;
401 } else if (mPrimaryBrowserParent
== aTab
) {
402 mPrimaryBrowserParent
= nullptr;
409 AppWindow::RemoteTabRemoved(nsIRemoteTab
* aTab
) {
410 if (aTab
== mPrimaryBrowserParent
) {
411 mPrimaryBrowserParent
= nullptr;
418 AppWindow::GetPrimaryRemoteTab(nsIRemoteTab
** aTab
) {
419 nsCOMPtr
<nsIRemoteTab
> tab
= mPrimaryBrowserParent
;
425 AppWindow::GetPrimaryContentBrowsingContext(
426 mozilla::dom::BrowsingContext
** aBc
) {
427 if (mPrimaryBrowserParent
) {
428 return mPrimaryBrowserParent
->GetBrowsingContext(aBc
);
430 if (mPrimaryContentShell
) {
431 return mPrimaryContentShell
->GetBrowsingContextXPCOM(aBc
);
437 static LayoutDeviceIntSize
GetOuterToInnerSizeDifference(nsIWidget
* aWindow
) {
439 return LayoutDeviceIntSize();
441 return aWindow
->ClientToWindowSizeDifference();
444 static CSSIntSize
GetOuterToInnerSizeDifferenceInCSSPixels(
445 nsIWidget
* aWindow
, CSSToLayoutDeviceScale aScale
) {
446 LayoutDeviceIntSize devPixelSize
= GetOuterToInnerSizeDifference(aWindow
);
447 return RoundedToInt(devPixelSize
/ aScale
);
451 AppWindow::GetOuterToInnerHeightDifferenceInCSSPixels(uint32_t* aResult
) {
452 *aResult
= GetOuterToInnerSizeDifferenceInCSSPixels(
453 mWindow
, UnscaledDevicePixelsPerCSSPixel())
459 AppWindow::GetOuterToInnerWidthDifferenceInCSSPixels(uint32_t* aResult
) {
460 *aResult
= GetOuterToInnerSizeDifferenceInCSSPixels(
461 mWindow
, UnscaledDevicePixelsPerCSSPixel())
466 nsTArray
<RefPtr
<mozilla::LiveResizeListener
>>
467 AppWindow::GetLiveResizeListeners() {
468 nsTArray
<RefPtr
<mozilla::LiveResizeListener
>> listeners
;
469 if (mPrimaryBrowserParent
) {
470 BrowserHost
* host
= BrowserHost::GetFrom(mPrimaryBrowserParent
.get());
471 RefPtr
<mozilla::LiveResizeListener
> actor
= host
->GetActor();
473 listeners
.AppendElement(actor
);
479 NS_IMETHODIMP
AppWindow::ShowModal() {
480 AUTO_PROFILER_LABEL("AppWindow::ShowModal", OTHER
);
482 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed
)) {
483 MOZ_ASSERT_UNREACHABLE(
484 "Trying to show modal window after shutdown started.");
485 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN
;
488 // Store locally so it doesn't die on us
489 nsCOMPtr
<nsIWidget
> window
= mWindow
;
490 nsCOMPtr
<nsIAppWindow
> tempRef
= this;
492 #ifdef USE_NATIVE_MENUS
493 if (!gfxPlatform::IsHeadless()) {
494 // On macOS, for modals created early in startup. (e.g.
495 // ProfileManager/ProfileDowngrade) this creates a fallback menu for the
496 // menu bar which only contains a "Quit" menu item. This allows the user to
497 // quit the application in a regular way with cmd+Q.
498 widget::NativeMenuSupport::CreateNativeMenuBar(mWindow
, nullptr);
502 window
->SetModal(true);
503 mContinueModalLoop
= true;
508 SpinEventLoopUntil("AppWindow::ShowModal"_ns
, [&]() {
510 AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed
))) {
511 // TODO: Bug 1699041 would apply also here: Should we return an error
512 // if we are bailing out from a pre-existing modal dialog for shutdown?
513 ExitModalLoop(NS_OK
);
515 return !mContinueModalLoop
;
519 mContinueModalLoop
= false;
520 window
->SetModal(false);
521 /* Note there's no EnableParent(true) here to match the false one
522 above. That's done in ExitModalLoop. It's important that the parent
523 be re-enabled before this window is made invisible; to do otherwise
524 causes bizarre z-ordering problems. At this point, the window is
526 No known current implementation of Enable would have a problem with
527 re-enabling the parent twice, so we could do it again here without
528 breaking any current implementation. But that's unnecessary if the
529 modal loop is always exited using ExitModalLoop (the other way would be
530 to change the protected member variable directly.)
536 //*****************************************************************************
537 // AppWindow::nsIBaseWindow
538 //*****************************************************************************
540 NS_IMETHODIMP
AppWindow::InitWindow(nativeWindow aParentNativeWindow
,
541 nsIWidget
* parentWidget
, int32_t x
,
542 int32_t y
, int32_t cx
, int32_t cy
) {
543 // XXX First Check In
544 NS_ASSERTION(false, "Not Yet Implemented");
548 NS_IMETHODIMP
AppWindow::Destroy() {
549 nsCOMPtr
<nsIAppWindow
> kungFuDeathGrip(this);
552 mDocShell
->RemoveProgressListener(this);
557 SavePersistentAttributes();
561 if (!mWindow
) return NS_OK
;
563 // Ensure we don't reenter this code
564 if (mDestroying
) return NS_OK
;
566 mozilla::AutoRestore
<bool> guard(mDestroying
);
569 nsCOMPtr
<nsIAppShellService
> appShell(
570 do_GetService(NS_APPSHELLSERVICE_CONTRACTID
));
571 NS_ASSERTION(appShell
, "Couldn't get appShell... xpcom shutdown?");
573 appShell
->UnregisterTopLevelWindow(static_cast<nsIAppWindow
*>(this));
576 // Remove modality (if any) and hide while destroying. More than
577 // a convenience, the hide prevents user interaction with the partially
578 // destroyed window. This is especially necessary when the eldest window
579 // in a stack of modal windows is destroyed first. It happens.
580 ExitModalLoop(NS_OK
);
581 // XXX: Skip unmapping the window on Linux due to GLX hangs on the compositor
582 // thread with NVIDIA driver 310.32. We don't need to worry about user
583 // interactions with destroyed windows on X11 either.
584 #ifndef MOZ_WIDGET_GTK
585 if (mWindow
) mWindow
->Show(false);
589 // We need to explicitly set the focus on Windows, but
590 // only if the parent is visible.
591 nsCOMPtr
<nsIBaseWindow
> parent(do_QueryReferent(mParentWindow
));
593 nsCOMPtr
<nsIWidget
> parentWidget
;
594 parent
->GetMainWidget(getter_AddRefs(parentWidget
));
596 if (parentWidget
&& parentWidget
->IsVisible()) {
597 bool isParentHiddenWindow
= false;
600 bool hasHiddenWindow
= false;
601 appShell
->GetHasHiddenWindow(&hasHiddenWindow
);
602 if (hasHiddenWindow
) {
603 nsCOMPtr
<nsIBaseWindow
> baseHiddenWindow
;
604 nsCOMPtr
<nsIAppWindow
> hiddenWindow
;
605 appShell
->GetHiddenWindow(getter_AddRefs(hiddenWindow
));
607 baseHiddenWindow
= do_GetInterface(hiddenWindow
);
608 isParentHiddenWindow
= (baseHiddenWindow
== parent
);
613 // somebody screwed up somewhere. hiddenwindow shouldn't be anybody's
614 // parent. still, when it happens, skip activating it.
615 if (!isParentHiddenWindow
) {
616 parentWidget
->PlaceBehind(eZPlacementTop
, 0, true);
622 RemoveTooltipSupport();
624 mDOMWindow
= nullptr;
626 RefPtr
<BrowsingContext
> bc(mDocShell
->GetBrowsingContext());
627 mDocShell
->Destroy();
629 mDocShell
= nullptr; // this can cause reentrancy of this function
632 mPrimaryContentShell
= nullptr;
634 if (mContentTreeOwner
) {
635 mContentTreeOwner
->AppWindow(nullptr);
636 NS_RELEASE(mContentTreeOwner
);
638 if (mPrimaryContentTreeOwner
) {
639 mPrimaryContentTreeOwner
->AppWindow(nullptr);
640 NS_RELEASE(mPrimaryContentTreeOwner
);
642 if (mChromeTreeOwner
) {
643 mChromeTreeOwner
->AppWindow(nullptr);
644 NS_RELEASE(mChromeTreeOwner
);
647 mWindow
->SetWidgetListener(nullptr); // nsWebShellWindow hackery
652 if (!mIsHiddenWindow
&& mRegistered
) {
653 /* Inform appstartup we've destroyed this window and it could
654 quit now if it wanted. This must happen at least after mDocShell
655 is destroyed, because onunload handlers fire then, and those being
656 script, anything could happen. A new window could open, even.
658 nsCOMPtr
<nsIObserverService
> obssvc
= services::GetObserverService();
659 NS_ASSERTION(obssvc
, "Couldn't get observer service?");
662 obssvc
->NotifyObservers(nullptr, "xul-window-destroyed", nullptr);
668 NS_IMETHODIMP
AppWindow::GetDevicePixelsPerDesktopPixel(double* aScale
) {
669 *aScale
= mWindow
? mWindow
->GetDesktopToDeviceScale().scale
: 1.0;
673 double AppWindow::GetWidgetCSSToDeviceScale() {
674 return mWindow
? mWindow
->GetDefaultScale().scale
: 1.0;
677 NS_IMETHODIMP
AppWindow::SetPositionDesktopPix(int32_t aX
, int32_t aY
) {
678 return MoveResize(Some(DesktopIntPoint(aX
, aY
)), Nothing(), false);
681 // The parameters here are device pixels; do the best we can to convert to
682 // desktop px, using the window's current scale factor (if available).
683 NS_IMETHODIMP
AppWindow::SetPosition(int32_t aX
, int32_t aY
) {
684 // Don't reset the window's size mode here - platforms that don't want to move
685 // maximized windows should reset it in their respective Move implementation.
686 return MoveResize(Some(LayoutDeviceIntPoint(aX
, aY
)), Nothing(), false);
689 NS_IMETHODIMP
AppWindow::GetPosition(int32_t* aX
, int32_t* aY
) {
690 return GetPositionAndSize(aX
, aY
, nullptr, nullptr);
693 NS_IMETHODIMP
AppWindow::SetSize(int32_t aCX
, int32_t aCY
, bool aRepaint
) {
694 /* any attempt to set the window's size or position overrides the window's
695 zoom state. this is important when these two states are competing while
696 the window is being opened. but it should probably just always be so. */
697 return MoveResize(Nothing(), Some(LayoutDeviceIntSize(aCX
, aCY
)), aRepaint
);
700 NS_IMETHODIMP
AppWindow::GetSize(int32_t* aCX
, int32_t* aCY
) {
701 return GetPositionAndSize(nullptr, nullptr, aCX
, aCY
);
704 NS_IMETHODIMP
AppWindow::SetPositionAndSize(int32_t aX
, int32_t aY
, int32_t aCX
,
705 int32_t aCY
, uint32_t aFlags
) {
706 /* any attempt to set the window's size or position overrides the window's
707 zoom state. this is important when these two states are competing while
708 the window is being opened. but it should probably just always be so. */
709 return MoveResize(Some(LayoutDeviceIntPoint(aX
, aY
)),
710 Some(LayoutDeviceIntSize(aCX
, aCY
)),
711 !!(aFlags
& nsIBaseWindow::eRepaint
));
714 NS_IMETHODIMP
AppWindow::GetPositionAndSize(int32_t* x
, int32_t* y
, int32_t* cx
,
716 if (!mWindow
) return NS_ERROR_FAILURE
;
718 LayoutDeviceIntRect rect
= mWindow
->GetScreenBounds();
720 if (x
) *x
= rect
.X();
721 if (y
) *y
= rect
.Y();
722 if (cx
) *cx
= rect
.Width();
723 if (cy
) *cy
= rect
.Height();
729 AppWindow::SetDimensions(DimensionRequest
&& aRequest
) {
730 if (aRequest
.mDimensionKind
== DimensionKind::Inner
) {
731 // For the chrome the inner size is the root shell size, and for the
732 // content it's the primary content size. We lack an indicator here that
733 // would allow us to distinguish between the two.
734 return NS_ERROR_NOT_IMPLEMENTED
;
737 MOZ_TRY(aRequest
.SupplementFrom(this));
738 return aRequest
.ApplyOuterTo(this);
742 AppWindow::GetDimensions(DimensionKind aDimensionKind
, int32_t* aX
, int32_t* aY
,
743 int32_t* aCX
, int32_t* aCY
) {
744 if (aDimensionKind
== DimensionKind::Inner
) {
745 // For the chrome the inner size is the root shell size, and for the
746 // content it's the primary content size. We lack an indicator here that
747 // would allow us to distinguish between the two.
748 return NS_ERROR_NOT_IMPLEMENTED
;
750 return GetPositionAndSize(aX
, aY
, aCX
, aCY
);
753 nsresult
AppWindow::MoveResize(const Maybe
<LayoutDeviceIntPoint
>& aPosition
,
754 const Maybe
<LayoutDeviceIntSize
>& aSize
,
756 DesktopToLayoutDeviceScale scale
= mWindow
->GetDesktopToDeviceScale();
758 return MoveResize(aPosition
? Some(*aPosition
/ scale
) : Nothing(),
759 aSize
? Some(*aSize
/ scale
) : Nothing(), aRepaint
);
762 nsresult
AppWindow::MoveResize(const Maybe
<DesktopPoint
>& aPosition
,
763 const Maybe
<DesktopSize
>& aSize
, bool aRepaint
) {
764 NS_ENSURE_STATE(mWindow
);
765 PersistentAttributes dirtyAttributes
;
767 if (!aPosition
&& !aSize
) {
768 MOZ_ASSERT_UNREACHABLE("Doing nothing?");
769 return NS_ERROR_UNEXPECTED
;
773 mWindow
->SetSizeMode(nsSizeMode_Normal
);
774 mIntrinsicallySized
= false;
775 mDominantClientSize
= false;
778 if (aPosition
&& aSize
) {
779 mWindow
->Resize(aPosition
->x
, aPosition
->y
, aSize
->width
, aSize
->height
,
781 dirtyAttributes
= {PersistentAttribute::Size
,
782 PersistentAttribute::Position
};
784 mWindow
->Resize(aSize
->width
, aSize
->height
, aRepaint
);
785 dirtyAttributes
= {PersistentAttribute::Size
};
786 } else if (aPosition
) {
787 mWindow
->Move(aPosition
->x
, aPosition
->y
);
788 dirtyAttributes
= {PersistentAttribute::Position
};
791 if (mSizingShellFromXUL
) {
792 // If we're invoked for sizing from XUL, we want to neither ignore anything
793 // nor persist anything, since it's already the value in XUL.
796 if (!mChromeLoaded
) {
797 // If we're called before the chrome is loaded someone obviously wants this
798 // window at this size & in the normal size mode (since it is the only mode
799 // in which setting dimensions makes sense). We don't persist this one-time
802 mIgnoreXULPosition
= true;
805 mIgnoreXULSize
= true;
806 mIgnoreXULSizeMode
= true;
811 PersistentAttributesDirty(dirtyAttributes
, Sync
);
815 NS_IMETHODIMP
AppWindow::Center(nsIAppWindow
* aRelative
, bool aScreen
,
818 bool screenCoordinates
= false, windowCoordinates
= false;
821 if (!mChromeLoaded
) {
822 // note we lose the parameters. at time of writing, this isn't a problem.
823 mCenterAfterLoad
= true;
827 if (!aScreen
&& !aRelative
) return NS_ERROR_INVALID_ARG
;
829 nsCOMPtr
<nsIScreenManager
> screenmgr
=
830 do_GetService("@mozilla.org/gfx/screenmanager;1", &result
);
831 if (NS_FAILED(result
)) {
835 nsCOMPtr
<nsIScreen
> screen
;
838 nsCOMPtr
<nsIBaseWindow
> base(do_QueryInterface(aRelative
));
840 rect
= RoundedToInt(base
->GetPositionAndSize() /
841 base
->DevicePixelsPerDesktopPixel());
842 // if centering on screen, convert that to the corresponding screen
844 screen
= screenmgr
->ScreenForRect(rect
);
846 windowCoordinates
= true;
851 if (!mOpenerScreenRect
.IsEmpty()) {
852 screen
= screenmgr
->ScreenForRect(mOpenerScreenRect
);
854 screenmgr
->GetPrimaryScreen(getter_AddRefs(screen
));
858 if (aScreen
&& screen
) {
859 rect
= screen
->GetAvailRectDisplayPix();
860 screenCoordinates
= true;
863 if (!screenCoordinates
&& !windowCoordinates
) {
864 return NS_ERROR_FAILURE
;
867 NS_ASSERTION(mWindow
, "what, no window?");
868 const LayoutDeviceIntSize ourDevSize
= GetSize();
869 const DesktopIntSize ourSize
=
870 RoundedToInt(ourDevSize
/ DevicePixelsPerDesktopPixel());
873 DesktopIntPoint((rect
.width
- ourSize
.width
) / 2,
874 (rect
.height
- ourSize
.height
) / (aAlert
? 3 : 2));
875 if (windowCoordinates
) {
876 mWindow
->ConstrainPosition(newPos
);
879 SetPositionDesktopPix(newPos
.x
, newPos
.y
);
881 // If moving the window caused it to change size, re-do the centering.
882 if (GetSize() != ourDevSize
) {
883 return Center(aRelative
, aScreen
, aAlert
);
888 NS_IMETHODIMP
AppWindow::Repaint(bool aForce
) {
889 // XXX First Check In
890 NS_ASSERTION(false, "Not Yet Implemented");
894 NS_IMETHODIMP
AppWindow::GetParentWidget(nsIWidget
** aParentWidget
) {
895 NS_ENSURE_ARG_POINTER(aParentWidget
);
896 NS_ENSURE_STATE(mWindow
);
898 NS_IF_ADDREF(*aParentWidget
= mWindow
->GetParent());
902 NS_IMETHODIMP
AppWindow::SetParentWidget(nsIWidget
* aParentWidget
) {
903 // XXX First Check In
904 NS_ASSERTION(false, "Not Yet Implemented");
908 NS_IMETHODIMP
AppWindow::GetParentNativeWindow(
909 nativeWindow
* aParentNativeWindow
) {
910 NS_ENSURE_ARG_POINTER(aParentNativeWindow
);
912 nsCOMPtr
<nsIWidget
> parentWidget
;
913 NS_ENSURE_SUCCESS(GetParentWidget(getter_AddRefs(parentWidget
)),
917 *aParentNativeWindow
= parentWidget
->GetNativeData(NS_NATIVE_WIDGET
);
923 NS_IMETHODIMP
AppWindow::SetParentNativeWindow(
924 nativeWindow aParentNativeWindow
) {
925 // XXX First Check In
926 NS_ASSERTION(false, "Not Yet Implemented");
930 NS_IMETHODIMP
AppWindow::GetNativeHandle(nsAString
& aNativeHandle
) {
931 nsCOMPtr
<nsIWidget
> mainWidget
;
932 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(mainWidget
)),
936 nativeWindow nativeWindowPtr
= mainWidget
->GetNativeData(NS_NATIVE_WINDOW
);
937 /* the nativeWindow pointer is converted to and exposed as a string. This
938 is a more reliable way not to lose information (as opposed to JS
939 |Number| for instance) */
941 NS_ConvertASCIItoUTF16(nsPrintfCString("0x%p", nativeWindowPtr
));
947 NS_IMETHODIMP
AppWindow::GetVisibility(bool* aVisibility
) {
948 NS_ENSURE_ARG_POINTER(aVisibility
);
950 // Always claim to be visible for now. See bug
951 // https://bugzilla.mozilla.org/show_bug.cgi?id=306245.
958 NS_IMETHODIMP
AppWindow::SetVisibility(bool aVisibility
) {
959 if (!mChromeLoaded
) {
960 mShowAfterLoad
= aVisibility
;
968 NS_ENSURE_STATE(mDocShell
);
970 mDebuting
= true; // (Show / Focus is recursive)
972 // XXXTAB Do we really need to show docshell and the window? Isn't
973 // the window good enough?
974 mDocShell
->SetVisibility(aVisibility
);
975 // Store locally so it doesn't die on us. 'Show' can result in the window
976 // being closed with AppWindow::Destroy being called. That would set
977 // mWindow to null and posibly destroy the nsIWidget while its Show method
978 // is on the stack. We need to keep it alive until Show finishes.
979 nsCOMPtr
<nsIWidget
> window
= mWindow
;
980 window
->Show(aVisibility
);
982 nsCOMPtr
<nsIWindowMediator
> windowMediator(
983 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
985 windowMediator
->UpdateWindowTimeStamp(static_cast<nsIAppWindow
*>(this));
987 // notify observers so that we can hide the splash screen if possible
988 nsCOMPtr
<nsIObserverService
> obssvc
= services::GetObserverService();
989 NS_ASSERTION(obssvc
, "Couldn't get observer service.");
991 obssvc
->NotifyObservers(static_cast<nsIAppWindow
*>(this),
992 "xul-window-visible", nullptr);
999 NS_IMETHODIMP
AppWindow::GetEnabled(bool* aEnabled
) {
1000 NS_ENSURE_ARG_POINTER(aEnabled
);
1003 *aEnabled
= mWindow
->IsEnabled();
1007 *aEnabled
= true; // better guess than most
1008 return NS_ERROR_FAILURE
;
1011 NS_IMETHODIMP
AppWindow::SetEnabled(bool aEnable
) {
1013 mWindow
->Enable(aEnable
);
1016 return NS_ERROR_FAILURE
;
1019 NS_IMETHODIMP
AppWindow::GetMainWidget(nsIWidget
** aMainWidget
) {
1020 NS_ENSURE_ARG_POINTER(aMainWidget
);
1021 NS_IF_ADDREF(*aMainWidget
= mWindow
);
1025 NS_IMETHODIMP
AppWindow::GetTitle(nsAString
& aTitle
) {
1030 NS_IMETHODIMP
AppWindow::SetTitle(const nsAString
& aTitle
) {
1031 NS_ENSURE_STATE(mWindow
);
1032 mTitle
.Assign(aTitle
);
1034 NS_ENSURE_SUCCESS(mWindow
->SetTitle(mTitle
), NS_ERROR_FAILURE
);
1038 //*****************************************************************************
1039 // AppWindow: Helpers
1040 //*****************************************************************************
1042 NS_IMETHODIMP
AppWindow::EnsureChromeTreeOwner() {
1043 if (mChromeTreeOwner
) return NS_OK
;
1045 mChromeTreeOwner
= new nsChromeTreeOwner();
1046 NS_ADDREF(mChromeTreeOwner
);
1047 mChromeTreeOwner
->AppWindow(this);
1052 NS_IMETHODIMP
AppWindow::EnsureContentTreeOwner() {
1053 if (mContentTreeOwner
) return NS_OK
;
1055 mContentTreeOwner
= new nsContentTreeOwner(false);
1056 NS_ADDREF(mContentTreeOwner
);
1057 mContentTreeOwner
->AppWindow(this);
1062 NS_IMETHODIMP
AppWindow::EnsurePrimaryContentTreeOwner() {
1063 if (mPrimaryContentTreeOwner
) return NS_OK
;
1065 mPrimaryContentTreeOwner
= new nsContentTreeOwner(true);
1066 NS_ADDREF(mPrimaryContentTreeOwner
);
1067 mPrimaryContentTreeOwner
->AppWindow(this);
1072 NS_IMETHODIMP
AppWindow::EnsurePrompter() {
1073 if (mPrompter
) return NS_OK
;
1075 nsCOMPtr
<mozIDOMWindowProxy
> ourWindow
;
1076 nsresult rv
= GetWindowDOMWindow(getter_AddRefs(ourWindow
));
1077 if (NS_SUCCEEDED(rv
)) {
1078 nsCOMPtr
<nsIWindowWatcher
> wwatch
=
1079 do_GetService(NS_WINDOWWATCHER_CONTRACTID
);
1080 if (wwatch
) wwatch
->GetNewPrompter(ourWindow
, getter_AddRefs(mPrompter
));
1082 return mPrompter
? NS_OK
: NS_ERROR_FAILURE
;
1085 NS_IMETHODIMP
AppWindow::EnsureAuthPrompter() {
1086 if (mAuthPrompter
) return NS_OK
;
1088 nsCOMPtr
<mozIDOMWindowProxy
> ourWindow
;
1089 nsresult rv
= GetWindowDOMWindow(getter_AddRefs(ourWindow
));
1090 if (NS_SUCCEEDED(rv
)) {
1091 nsCOMPtr
<nsIWindowWatcher
> wwatch(
1092 do_GetService(NS_WINDOWWATCHER_CONTRACTID
));
1094 wwatch
->GetNewAuthPrompter(ourWindow
, getter_AddRefs(mAuthPrompter
));
1096 return mAuthPrompter
? NS_OK
: NS_ERROR_FAILURE
;
1099 NS_IMETHODIMP
AppWindow::GetAvailScreenSize(int32_t* aAvailWidth
,
1100 int32_t* aAvailHeight
) {
1101 nsCOMPtr
<mozIDOMWindowProxy
> domWindow
;
1102 GetWindowDOMWindow(getter_AddRefs(domWindow
));
1103 NS_ENSURE_STATE(domWindow
);
1105 auto* window
= nsGlobalWindowOuter::Cast(domWindow
);
1107 RefPtr
<nsScreen
> screen
= window
->GetScreen();
1108 NS_ENSURE_STATE(screen
);
1110 *aAvailWidth
= screen
->AvailWidth();
1111 *aAvailHeight
= screen
->AvailHeight();
1115 // Rounds window size to 1000x1000, or, if there isn't enough available
1116 // screen space, to a multiple of 200x100.
1117 NS_IMETHODIMP
AppWindow::ForceRoundedDimensions() {
1118 if (mIsHiddenWindow
) {
1122 CSSToLayoutDeviceScale scale
= UnscaledDevicePixelsPerCSSPixel();
1124 CSSIntSize availSizeCSS
;
1125 GetAvailScreenSize(&availSizeCSS
.width
, &availSizeCSS
.height
);
1127 // To get correct chrome size, we have to resize the window to a proper
1128 // size first. So, here, we size it to its available size.
1129 SetSpecifiedSize(availSizeCSS
.width
, availSizeCSS
.height
);
1131 // Get the current window size for calculating chrome UI size.
1132 CSSIntSize windowSizeCSS
= RoundedToInt(GetSize() / scale
);
1134 // Get the content size for calculating chrome UI size.
1135 LayoutDeviceIntSize contentSizeDev
;
1136 GetPrimaryContentSize(&contentSizeDev
.width
, &contentSizeDev
.height
);
1137 CSSIntSize contentSizeCSS
= RoundedToInt(contentSizeDev
/ scale
);
1139 // Calculate the chrome UI size.
1140 CSSIntSize chromeSizeCSS
= windowSizeCSS
- contentSizeCSS
;
1142 CSSIntSize targetSizeCSS
;
1143 // Here, we use the available screen dimensions as the input dimensions to
1144 // force the window to be rounded as the maximum available content size.
1145 nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
1146 chromeSizeCSS
.width
, chromeSizeCSS
.height
, availSizeCSS
.width
,
1147 availSizeCSS
.height
, availSizeCSS
.width
, availSizeCSS
.height
,
1148 false, // aSetOuterWidth
1149 false, // aSetOuterHeight
1150 &targetSizeCSS
.width
, &targetSizeCSS
.height
);
1152 LayoutDeviceIntSize targetSizeDev
= RoundedToInt(targetSizeCSS
* scale
);
1154 SetPrimaryContentSize(targetSizeDev
.width
, targetSizeDev
.height
);
1159 void AppWindow::OnChromeLoaded() {
1160 nsresult rv
= EnsureContentTreeOwner();
1162 if (NS_SUCCEEDED(rv
)) {
1163 mChromeLoaded
= true;
1165 SyncAttributesToWidget();
1168 if (mShowAfterLoad
) {
1169 SetVisibility(true);
1171 AddTooltipSupport();
1173 // At this point the window may have been closed already during Show() or
1174 // SyncAttributesToWidget(), so AppWindow::Destroy may already have been
1175 // called. Take care!
1177 mPersistentAttributesMask
+= AllPersistentAttributes();
1180 bool AppWindow::NeedsTooltipListener() {
1181 nsCOMPtr
<dom::Element
> docShellElement
= GetWindowDOMElement();
1182 if (!docShellElement
|| docShellElement
->IsXULElement()) {
1183 // Tooltips in XUL are handled by each element.
1186 // All other non-XUL document types need a tooltip listener.
1190 void AppWindow::AddTooltipSupport() {
1191 if (!NeedsTooltipListener()) {
1194 nsXULTooltipListener
* listener
= nsXULTooltipListener::GetInstance();
1199 nsCOMPtr
<dom::Element
> docShellElement
= GetWindowDOMElement();
1200 MOZ_ASSERT(docShellElement
);
1201 listener
->AddTooltipSupport(docShellElement
);
1204 void AppWindow::RemoveTooltipSupport() {
1205 if (!NeedsTooltipListener()) {
1208 nsXULTooltipListener
* listener
= nsXULTooltipListener::GetInstance();
1213 nsCOMPtr
<dom::Element
> docShellElement
= GetWindowDOMElement();
1214 MOZ_ASSERT(docShellElement
);
1215 listener
->RemoveTooltipSupport(docShellElement
);
1218 static Maybe
<int32_t> ReadIntAttribute(const Element
& aElement
,
1220 nsAtom
* aSecondary
= nullptr) {
1221 nsAutoString attrString
;
1222 if (!aElement
.GetAttr(aPrimary
, attrString
)) {
1224 return ReadIntAttribute(aElement
, aSecondary
);
1229 nsresult res
= NS_OK
;
1230 int32_t ret
= attrString
.ToInteger(&res
);
1231 return NS_SUCCEEDED(res
) ? Some(ret
) : Nothing();
1234 // If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes
1235 // to fit to the screen when staggering windows; if they're negative,
1236 // we use the window's current size instead.
1237 bool AppWindow::LoadPositionFromXUL(int32_t aSpecWidth
, int32_t aSpecHeight
) {
1238 bool gotPosition
= false;
1240 // if we're the hidden window, don't try to validate our size/position. We're
1242 if (mIsHiddenWindow
) {
1246 RefPtr
<dom::Element
> root
= GetWindowDOMElement();
1247 NS_ENSURE_TRUE(root
, false);
1249 const LayoutDeviceIntRect devRect
= GetPositionAndSize();
1251 // Convert to global display pixels for consistent window management across
1252 // screens with diverse resolutions
1253 const DesktopIntPoint curPoint
=
1254 RoundedToInt(devRect
.TopLeft() / DevicePixelsPerDesktopPixel());
1256 // For size, use specified value if > 0, else current value
1257 CSSIntSize
cssSize(aSpecWidth
, aSpecHeight
);
1259 CSSIntSize currentSize
=
1260 RoundedToInt(devRect
.Size() / UnscaledDevicePixelsPerCSSPixel());
1261 if (aSpecHeight
<= 0) {
1262 cssSize
.height
= currentSize
.height
;
1264 if (aSpecWidth
<= 0) {
1265 cssSize
.width
= currentSize
.width
;
1269 // Obtain the position information from the <xul:window> element.
1270 DesktopIntPoint specPoint
= curPoint
;
1272 // Also read lowercase screenx/y because the front-end sometimes sets these
1273 // via setAttribute on HTML documents like about:blank, and stuff gets
1276 // TODO(emilio): We should probably rename screenX/Y to screen-x/y to
1277 // prevent this impedance mismatch.
1279 ReadIntAttribute(*root
, nsGkAtoms::screenX
, nsGkAtoms::screenx
)) {
1280 specPoint
.x
= *attr
;
1285 ReadIntAttribute(*root
, nsGkAtoms::screenY
, nsGkAtoms::screeny
)) {
1286 specPoint
.y
= *attr
;
1291 // Our position will be relative to our parent, if any
1292 nsCOMPtr
<nsIBaseWindow
> parent(do_QueryReferent(mParentWindow
));
1294 const DesktopIntPoint parentPos
= RoundedToInt(
1295 parent
->GetPosition() / parent
->DevicePixelsPerDesktopPixel());
1296 specPoint
+= parentPos
;
1298 StaggerPosition(specPoint
.x
.value
, specPoint
.y
.value
, cssSize
.width
,
1302 mWindow
->ConstrainPosition(specPoint
);
1303 if (specPoint
!= curPoint
) {
1304 SetPositionDesktopPix(specPoint
.x
, specPoint
.y
);
1310 static Maybe
<int32_t> ReadSize(const Element
& aElement
, nsAtom
* aAttr
,
1311 nsAtom
* aMinAttr
, nsAtom
* aMaxAttr
) {
1312 Maybe
<int32_t> attr
= ReadIntAttribute(aElement
, aAttr
);
1318 std::max(100, ReadIntAttribute(aElement
, aMinAttr
).valueOr(100));
1319 int32_t max
= ReadIntAttribute(aElement
, aMaxAttr
)
1320 .valueOr(std::numeric_limits
<int32_t>::max());
1322 return Some(std::min(max
, std::max(*attr
, min
)));
1325 bool AppWindow::LoadSizeFromXUL(int32_t& aSpecWidth
, int32_t& aSpecHeight
) {
1326 bool gotSize
= false;
1328 // if we're the hidden window, don't try to validate our size/position. We're
1330 if (mIsHiddenWindow
) {
1334 nsCOMPtr
<dom::Element
> windowElement
= GetWindowDOMElement();
1335 NS_ENSURE_TRUE(windowElement
, false);
1337 // Obtain the sizing information from the <xul:window> element.
1341 if (auto width
= ReadSize(*windowElement
, nsGkAtoms::width
,
1342 nsGkAtoms::minwidth
, nsGkAtoms::maxwidth
)) {
1343 aSpecWidth
= *width
;
1347 if (auto height
= ReadSize(*windowElement
, nsGkAtoms::height
,
1348 nsGkAtoms::minheight
, nsGkAtoms::maxheight
)) {
1349 aSpecHeight
= *height
;
1356 void AppWindow::SetSpecifiedSize(int32_t aSpecWidth
, int32_t aSpecHeight
) {
1357 // These are in CSS pixels of the main window.
1358 // TODO(emilio): In my testing we usually have a pres context around, can we
1359 // just use it? That'd simplify the coordinate calculations.
1361 int32_t screenWidth
;
1362 int32_t screenHeight
;
1364 if (NS_SUCCEEDED(GetAvailScreenSize(&screenWidth
, &screenHeight
))) {
1365 if (aSpecWidth
> screenWidth
) {
1366 aSpecWidth
= screenWidth
;
1368 if (aSpecHeight
> screenHeight
) {
1369 aSpecHeight
= screenHeight
;
1374 NS_ASSERTION(mWindow
, "we expected to have a window already");
1376 mIntrinsicallySized
= false;
1378 // Convert specified values to device pixels, and resize
1379 auto newSize
= RoundedToInt(CSSIntSize(aSpecWidth
, aSpecHeight
) *
1380 UnscaledDevicePixelsPerCSSPixel());
1382 // Note: Because of the asynchronous resizing on Linux we have to call
1383 // SetSize even when the size doesn't appear to change. A previous call that
1384 // has yet to complete can still change the size. We want the latest call to
1385 // define the final size.
1386 SetSize(newSize
.width
, newSize
.height
, false);
1389 /* Miscellaneous persistent attributes are attributes named in the
1390 |persist| attribute, other than size and position. Those are special
1391 because it's important to load those before one of the misc
1392 attributes (sizemode) and they require extra processing. */
1393 bool AppWindow::UpdateWindowStateFromMiscXULAttributes() {
1394 bool gotState
= false;
1396 /* There are no misc attributes of interest to the hidden window.
1397 It's especially important not to try to validate that window's
1398 size or position, because some platforms (Mac OS X) need to
1399 make it visible and offscreen. */
1400 if (mIsHiddenWindow
) return false;
1402 nsCOMPtr
<dom::Element
> windowElement
= GetWindowDOMElement();
1403 NS_ENSURE_TRUE(windowElement
, false);
1405 nsAutoString stateString
;
1406 nsSizeMode sizeMode
= nsSizeMode_Normal
;
1408 // If we are told to ignore the size mode attribute, force
1410 if (mIgnoreXULSizeMode
) {
1411 windowElement
->SetAttr(nsGkAtoms::sizemode
, SIZEMODE_NORMAL
,
1414 // Otherwise, read sizemode from DOM and, if the window is resizable,
1416 windowElement
->GetAttr(nsGkAtoms::sizemode
, stateString
);
1417 if ((stateString
.Equals(SIZEMODE_MAXIMIZED
) ||
1418 stateString
.Equals(SIZEMODE_FULLSCREEN
))) {
1419 /* Honor request to maximize only if the window is sizable.
1420 An unsizable, unmaximizable, yet maximized window confuses
1421 Windows OS and is something of a travesty, anyway. */
1422 if (mChromeFlags
& nsIWebBrowserChrome::CHROME_WINDOW_RESIZE
) {
1423 mIntrinsicallySized
= false;
1425 if (stateString
.Equals(SIZEMODE_MAXIMIZED
))
1426 sizeMode
= nsSizeMode_Maximized
;
1428 sizeMode
= nsSizeMode_Fullscreen
;
1433 if (sizeMode
== nsSizeMode_Fullscreen
) {
1434 nsCOMPtr
<mozIDOMWindowProxy
> ourWindow
;
1435 GetWindowDOMWindow(getter_AddRefs(ourWindow
));
1436 auto* piWindow
= nsPIDOMWindowOuter::From(ourWindow
);
1437 piWindow
->SetFullScreen(true);
1439 // For maximized windows, ignore the XUL size and position attributes,
1440 // as setting them would set the window back to normal sizemode.
1441 if (sizeMode
== nsSizeMode_Maximized
) {
1442 mIgnoreXULSize
= true;
1443 mIgnoreXULPosition
= true;
1445 mWindow
->SetSizeMode(sizeMode
);
1450 windowElement
->GetAttr(nsGkAtoms::zlevel
, stateString
);
1451 if (!stateString
.IsEmpty()) {
1453 int32_t zLevel
= stateString
.ToInteger(&errorCode
);
1454 if (NS_SUCCEEDED(errorCode
) && zLevel
>= lowestZ
&& zLevel
<= highestZ
)
1461 /* Stagger windows of the same type so they don't appear on top of each other.
1462 This code does have a scary double loop -- it'll keep passing through
1463 the entire list of open windows until it finds a non-collision. Doesn't
1464 seem to be a problem, but it deserves watching.
1465 The aRequested{X,Y} parameters here are in desktop pixels;
1466 the aSpec{Width,Height} parameters are CSS pixel dimensions.
1468 void AppWindow::StaggerPosition(int32_t& aRequestedX
, int32_t& aRequestedY
,
1469 int32_t aSpecWidth
, int32_t aSpecHeight
) {
1470 // These "constants" will be converted from CSS to desktop pixels
1471 // for the appropriate screen, assuming we find a screen to use...
1472 // hence they're not actually declared const here.
1473 int32_t kOffset
= 22;
1477 int bouncedX
= 0, // bounced off vertical edge of screen
1478 bouncedY
= 0; // bounced off horizontal edge
1480 // look for any other windows of this type
1481 nsCOMPtr
<nsIWindowMediator
> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
1484 nsCOMPtr
<dom::Element
> windowElement
= GetWindowDOMElement();
1485 if (!windowElement
) return;
1487 nsCOMPtr
<nsIAppWindow
> ourAppWindow(this);
1489 nsAutoString windowType
;
1490 windowElement
->GetAttr(nsGkAtoms::windowtype
, windowType
);
1492 DesktopIntRect screenRect
;
1493 bool gotScreen
= false;
1495 { // fetch screen coordinates
1496 nsCOMPtr
<nsIScreenManager
> screenMgr(
1497 do_GetService("@mozilla.org/gfx/screenmanager;1"));
1499 nsCOMPtr
<nsIScreen
> ourScreen
;
1500 // The coordinates here are already display pixels
1501 // XXX aSpecWidth and aSpecHeight are CSS pixels!
1502 screenMgr
->ScreenForRect(aRequestedX
, aRequestedY
, aSpecWidth
,
1503 aSpecHeight
, getter_AddRefs(ourScreen
));
1505 screenRect
= ourScreen
->GetAvailRectDisplayPix();
1507 // Get the screen's scaling factors and convert staggering constants
1508 // from CSS px to desktop pixel units
1509 auto scale
= ourScreen
->GetCSSToDesktopScale();
1510 kOffset
= (CSSCoord(kOffset
) * scale
).Rounded();
1511 kSlop
= (CSSCoord(kSlop
) * scale
).Rounded();
1512 // Convert dimensions from CSS to desktop pixels
1513 aSpecWidth
= (CSSCoord(aSpecWidth
) * scale
).Rounded();
1514 aSpecHeight
= (CSSCoord(aSpecHeight
) * scale
).Rounded();
1520 // One full pass through all windows of this type, repeat until no collisions.
1523 nsCOMPtr
<nsISimpleEnumerator
> windowList
;
1524 wm
->GetAppWindowEnumerator(windowType
.get(), getter_AddRefs(windowList
));
1526 if (!windowList
) break;
1528 // One full pass through all windows of this type, offset and stop on
1532 windowList
->HasMoreElements(&more
);
1535 nsCOMPtr
<nsISupports
> supportsWindow
;
1536 windowList
->GetNext(getter_AddRefs(supportsWindow
));
1538 nsCOMPtr
<nsIAppWindow
> listAppWindow(do_QueryInterface(supportsWindow
));
1539 if (listAppWindow
!= ourAppWindow
) {
1540 int32_t listX
, listY
;
1541 nsCOMPtr
<nsIBaseWindow
> listBaseWindow(
1542 do_QueryInterface(supportsWindow
));
1543 listBaseWindow
->GetPosition(&listX
, &listY
);
1546 listBaseWindow
->GetDevicePixelsPerDesktopPixel(&scale
))) {
1547 listX
= NSToIntRound(listX
/ scale
);
1548 listY
= NSToIntRound(listY
/ scale
);
1551 if (Abs(listX
- aRequestedX
) <= kSlop
&&
1552 Abs(listY
- aRequestedY
) <= kSlop
) {
1553 // collision! offset and start over
1555 aRequestedX
-= kOffset
;
1557 aRequestedX
+= kOffset
;
1558 aRequestedY
+= kOffset
;
1561 // if we're moving to the right and we need to bounce...
1562 if (!(bouncedX
& 0x1) &&
1563 ((aRequestedX
+ aSpecWidth
) > screenRect
.XMost())) {
1564 aRequestedX
= screenRect
.XMost() - aSpecWidth
;
1568 // if we're moving to the left and we need to bounce...
1569 if ((bouncedX
& 0x1) && aRequestedX
< screenRect
.X()) {
1570 aRequestedX
= screenRect
.X();
1574 // if we hit the bottom then bounce to the top
1575 if (aRequestedY
+ aSpecHeight
> screenRect
.YMost()) {
1576 aRequestedY
= screenRect
.Y();
1581 /* loop around again,
1582 but it's time to give up once we've covered the screen.
1583 there's a potential infinite loop with lots of windows. */
1584 keepTrying
= bouncedX
< 2 || bouncedY
== 0;
1589 } while (keepTrying
);
1592 void AppWindow::SyncAttributesToWidget() {
1593 nsCOMPtr
<dom::Element
> windowElement
= GetWindowDOMElement();
1594 if (!windowElement
) return;
1596 MOZ_DIAGNOSTIC_ASSERT(mWindow
, "No widget on SyncAttributesToWidget?");
1600 // Some attributes can change the client size (e.g. chromemargin on Windows
1601 // and MacOS). But we might want to keep it.
1602 const LayoutDeviceIntSize oldClientSize
= mWindow
->GetClientSize();
1603 // We have to check now whether we want to restore the client size, as any
1604 // change in size will reset its state.
1605 bool maintainClientSize
= mDominantClientSize
;
1607 // "hidechrome" attribute
1608 if (windowElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::hidechrome
,
1609 nsGkAtoms::_true
, eCaseMatters
)) {
1610 mWindow
->HideWindowChrome(true);
1613 NS_ENSURE_TRUE_VOID(mWindow
);
1615 // "chromemargin" attribute
1616 nsIntMargin margins
;
1617 windowElement
->GetAttribute(u
"chromemargin"_ns
, attr
);
1618 if (nsContentUtils::ParseIntMarginValue(attr
, margins
)) {
1619 mWindow
->SetNonClientMargins(
1620 LayoutDeviceIntMargin::FromUnknownMargin(margins
));
1623 NS_ENSURE_TRUE_VOID(mWindow
);
1625 // "windowtype", "windowclass", "windowname" attributes
1626 nsAutoString windowClassAttr
, windowNameAttr
;
1627 windowElement
->GetAttr(nsGkAtoms::windowtype
, attr
);
1628 windowElement
->GetAttribute(u
"windowclass"_ns
, windowClassAttr
);
1629 windowElement
->GetAttribute(u
"windowname"_ns
, windowNameAttr
);
1630 mWindow
->SetWindowClass(attr
, windowClassAttr
, windowNameAttr
);
1632 NS_ENSURE_TRUE_VOID(mWindow
);
1635 windowElement
->GetAttribute(u
"icon"_ns
, attr
);
1636 if (!attr
.IsEmpty()) {
1637 mWindow
->SetIcon(attr
);
1639 NS_ENSURE_TRUE_VOID(mWindow
);
1642 // "drawtitle" attribute
1643 windowElement
->GetAttribute(u
"drawtitle"_ns
, attr
);
1644 mWindow
->SetDrawsTitle(attr
.LowerCaseEqualsLiteral("true"));
1646 NS_ENSURE_TRUE_VOID(mWindow
);
1648 // "toggletoolbar" attribute
1649 windowElement
->GetAttribute(u
"toggletoolbar"_ns
, attr
);
1650 mWindow
->SetShowsToolbarButton(attr
.LowerCaseEqualsLiteral("true"));
1652 NS_ENSURE_TRUE_VOID(mWindow
);
1654 // "macnativefullscreen" attribute
1655 windowElement
->GetAttribute(u
"macnativefullscreen"_ns
, attr
);
1656 mWindow
->SetSupportsNativeFullscreen(attr
.LowerCaseEqualsLiteral("true"));
1658 NS_ENSURE_TRUE_VOID(mWindow
);
1660 // "macanimationtype" attribute
1661 windowElement
->GetAttribute(u
"macanimationtype"_ns
, attr
);
1662 if (attr
.EqualsLiteral("document")) {
1663 mWindow
->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation
);
1666 // Check if the client size did change and if we want to restore it.
1667 if (maintainClientSize
&& mWindow
->SizeMode() == nsSizeMode_Normal
&&
1668 oldClientSize
!= mWindow
->GetClientSize()) {
1669 mWindow
->ResizeClient(oldClientSize
/ mWindow
->GetDesktopToDeviceScale(),
1671 mDominantClientSize
= true;
1675 enum class ConversionDirection
{
1680 static void ConvertWindowSize(nsIAppWindow
* aWin
, const nsAtom
* aAttr
,
1681 ConversionDirection aDirection
,
1682 nsAString
& aInOutString
) {
1684 MOZ_ASSERT(aAttr
== nsGkAtoms::width
|| aAttr
== nsGkAtoms::height
);
1687 int32_t size
= aInOutString
.ToInteger(&rv
);
1688 if (NS_FAILED(rv
)) {
1692 int32_t sizeDiff
= aAttr
== nsGkAtoms::width
1693 ? aWin
->GetOuterToInnerWidthDifferenceInCSSPixels()
1694 : aWin
->GetOuterToInnerHeightDifferenceInCSSPixels();
1700 int32_t multiplier
= aDirection
== ConversionDirection::InnerToOuter
? 1 : -1;
1702 CopyASCIItoUTF16(nsPrintfCString("%d", size
+ multiplier
* sizeDiff
),
1706 nsresult
AppWindow::GetPersistentValue(const nsAtom
* aAttr
, nsAString
& aValue
) {
1707 if (!XRE_IsParentProcess()) {
1708 // The XULStore is only available in the parent process.
1709 return NS_ERROR_UNEXPECTED
;
1712 nsCOMPtr
<dom::Element
> docShellElement
= GetWindowDOMElement();
1713 if (!docShellElement
) {
1714 return NS_ERROR_FAILURE
;
1717 nsAutoString windowElementId
;
1718 docShellElement
->GetId(windowElementId
);
1719 // Elements must have an ID to be persisted.
1720 if (windowElementId
.IsEmpty()) {
1724 RefPtr
<dom::Document
> ownerDoc
= docShellElement
->OwnerDoc();
1725 nsIURI
* docURI
= ownerDoc
->GetDocumentURI();
1727 return NS_ERROR_FAILURE
;
1729 nsAutoCString utf8uri
;
1730 nsresult rv
= docURI
->GetSpec(utf8uri
);
1731 NS_ENSURE_SUCCESS(rv
, rv
);
1732 NS_ConvertUTF8toUTF16
uri(utf8uri
);
1735 mLocalStore
= do_GetService("@mozilla.org/xul/xulstore;1");
1736 if (NS_WARN_IF(!mLocalStore
)) {
1737 return NS_ERROR_NOT_INITIALIZED
;
1741 rv
= mLocalStore
->GetValue(uri
, windowElementId
, nsDependentAtomString(aAttr
),
1743 if (NS_WARN_IF(NS_FAILED(rv
))) {
1747 if (aAttr
== nsGkAtoms::width
|| aAttr
== nsGkAtoms::height
) {
1748 // Convert attributes from outer size to inner size for top-level
1749 // windows, see bug 1444525 & co.
1750 ConvertWindowSize(this, aAttr
, ConversionDirection::OuterToInner
, aValue
);
1756 nsresult
AppWindow::GetDocXulStoreKeys(nsString
& aUriSpec
,
1757 nsString
& aWindowElementId
) {
1758 nsCOMPtr
<dom::Element
> docShellElement
= GetWindowDOMElement();
1759 if (!docShellElement
) {
1760 return NS_ERROR_FAILURE
;
1763 docShellElement
->GetId(aWindowElementId
);
1764 // Match the behavior of XULPersist and only persist values if the element
1766 if (aWindowElementId
.IsEmpty()) {
1770 RefPtr
<dom::Document
> ownerDoc
= docShellElement
->OwnerDoc();
1771 nsIURI
* docURI
= ownerDoc
->GetDocumentURI();
1773 return NS_ERROR_FAILURE
;
1776 nsAutoCString utf8uri
;
1777 nsresult rv
= docURI
->GetSpec(utf8uri
);
1778 if (NS_WARN_IF(NS_FAILED(rv
))) {
1782 aUriSpec
= NS_ConvertUTF8toUTF16(utf8uri
);
1787 nsresult
AppWindow::MaybeSaveEarlyWindowPersistentValues(
1788 const LayoutDeviceIntRect
& aRect
) {
1791 nsAutoString windowElementId
;
1792 nsresult rv
= GetDocXulStoreKeys(uri
, windowElementId
);
1794 if (NS_WARN_IF(NS_FAILED(rv
))) {
1798 if (!windowElementId
.EqualsLiteral("main-window") ||
1799 !uri
.EqualsLiteral("chrome://browser/content/browser.xhtml")) {
1803 SkeletonUISettings settings
;
1805 settings
.screenX
= aRect
.X();
1806 settings
.screenY
= aRect
.Y();
1807 settings
.width
= aRect
.Width();
1808 settings
.height
= aRect
.Height();
1810 settings
.maximized
= mWindow
->SizeMode() == nsSizeMode_Maximized
;
1811 settings
.cssToDevPixelScaling
= UnscaledDevicePixelsPerCSSPixel().scale
;
1813 nsCOMPtr
<dom::Element
> windowElement
= GetWindowDOMElement();
1814 Document
* doc
= windowElement
->GetComposedDoc();
1815 Element
* urlbarEl
= doc
->GetElementById(u
"urlbar"_ns
);
1817 nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocShell
->GetWindow();
1818 nsCOMPtr
<nsIDOMWindowUtils
> utils
=
1819 nsGlobalWindowOuter::Cast(window
)->WindowUtils();
1820 RefPtr
<dom::DOMRect
> urlbarRect
;
1821 rv
= utils
->GetBoundsWithoutFlushing(urlbarEl
, getter_AddRefs(urlbarRect
));
1822 if (NS_WARN_IF(NS_FAILED(rv
))) {
1826 double urlbarX
= urlbarRect
->X();
1827 double urlbarWidth
= urlbarRect
->Width();
1829 // Hard-coding the following values and this behavior in general is rather
1830 // fragile, and can easily get out of sync with the actual front-end values.
1831 // This is not intended as a long-term solution, but only as the relatively
1832 // straightforward implementation of an experimental feature. If we want to
1833 // ship the skeleton UI to all users, we should strongly consider a more
1834 // robust solution than this. The vertical position of the urlbar will be
1836 nsAutoString attributeValue
;
1837 urlbarEl
->GetAttribute(u
"breakout-extend"_ns
, attributeValue
);
1838 // Scale down the urlbar if it is focused
1839 if (attributeValue
.EqualsLiteral("true")) {
1840 // defined in browser.inc.css as 2px
1841 int urlbarBreakoutExtend
= 2;
1842 // defined in urlbar-searchbar.inc.css as 5px
1843 int urlbarMarginInline
= 5;
1845 // breakout-extend measurements are defined in urlbar-searchbar.inc.css
1846 urlbarX
+= (double)(urlbarBreakoutExtend
+ urlbarMarginInline
);
1847 urlbarWidth
-= (double)(2 * (urlbarBreakoutExtend
+ urlbarMarginInline
));
1849 CSSPixelSpan urlbar
;
1850 urlbar
.start
= urlbarX
;
1851 urlbar
.end
= urlbar
.start
+ urlbarWidth
;
1852 settings
.urlbarSpan
= urlbar
;
1854 Element
* navbar
= doc
->GetElementById(u
"nav-bar"_ns
);
1856 Element
* searchbarEl
= doc
->GetElementById(u
"searchbar"_ns
);
1857 CSSPixelSpan searchbar
;
1858 if (navbar
->Contains(searchbarEl
)) {
1859 RefPtr
<dom::DOMRect
> searchbarRect
;
1860 rv
= utils
->GetBoundsWithoutFlushing(searchbarEl
,
1861 getter_AddRefs(searchbarRect
));
1862 if (NS_WARN_IF(NS_FAILED(rv
))) {
1865 searchbar
.start
= searchbarRect
->X();
1866 searchbar
.end
= searchbar
.start
+ searchbarRect
->Width();
1868 // There is no searchbar in the UI
1869 searchbar
.start
= 0;
1872 settings
.searchbarSpan
= searchbar
;
1874 nsAutoString bookmarksVisibility
;
1875 Preferences::GetString("browser.toolbars.bookmarks.visibility",
1876 bookmarksVisibility
);
1877 settings
.bookmarksToolbarShown
=
1878 bookmarksVisibility
.EqualsLiteral("always") ||
1879 bookmarksVisibility
.EqualsLiteral("newtab");
1881 Element
* menubar
= doc
->GetElementById(u
"toolbar-menubar"_ns
);
1882 menubar
->GetAttribute(u
"autohide"_ns
, attributeValue
);
1883 settings
.menubarShown
= attributeValue
.EqualsLiteral("false");
1886 nsCOMPtr
<nsIHTMLCollection
> toolbarSprings
= navbar
->GetElementsByTagNameNS(
1887 u
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns
,
1888 u
"toolbarspring"_ns
, err
);
1890 return NS_ERROR_FAILURE
;
1892 mozilla::Vector
<CSSPixelSpan
> springs
;
1893 for (size_t i
= 0; i
< toolbarSprings
->Length(); i
++) {
1894 RefPtr
<Element
> springEl
= toolbarSprings
->Item(i
);
1895 RefPtr
<dom::DOMRect
> springRect
;
1896 rv
= utils
->GetBoundsWithoutFlushing(springEl
, getter_AddRefs(springRect
));
1897 if (NS_WARN_IF(NS_FAILED(rv
))) {
1900 CSSPixelSpan spring
;
1901 spring
.start
= springRect
->X();
1902 spring
.end
= spring
.start
+ springRect
->Width();
1903 if (!settings
.springs
.append(spring
)) {
1904 return NS_ERROR_FAILURE
;
1908 settings
.rtlEnabled
= intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1910 bool isInTabletMode
= false;
1911 bool autoTouchModePref
=
1912 Preferences::GetBool("browser.touchmode.auto", false);
1913 if (autoTouchModePref
) {
1914 nsCOMPtr
<nsIWindowsUIUtils
> uiUtils(
1915 do_GetService("@mozilla.org/windows-ui-utils;1"));
1916 if (!NS_WARN_IF(!uiUtils
)) {
1917 uiUtils
->GetInTabletMode(&isInTabletMode
);
1921 if (isInTabletMode
) {
1922 settings
.uiDensity
= SkeletonUIDensity::Touch
;
1924 int uiDensityPref
= Preferences::GetInt("browser.uidensity", 0);
1925 switch (uiDensityPref
) {
1927 settings
.uiDensity
= SkeletonUIDensity::Default
;
1931 settings
.uiDensity
= SkeletonUIDensity::Compact
;
1935 settings
.uiDensity
= SkeletonUIDensity::Touch
;
1941 Unused
<< PersistPreXULSkeletonUIValues(settings
);
1947 nsresult
AppWindow::SetPersistentValue(const nsAtom
* aAttr
,
1948 const nsAString
& aValue
) {
1949 if (!XRE_IsParentProcess()) {
1950 // The XULStore is only available in the parent process.
1951 return NS_ERROR_UNEXPECTED
;
1955 nsAutoString windowElementId
;
1956 nsresult rv
= GetDocXulStoreKeys(uri
, windowElementId
);
1958 if (NS_FAILED(rv
) || windowElementId
.IsEmpty()) {
1962 nsAutoString
maybeConvertedValue(aValue
);
1963 if (aAttr
== nsGkAtoms::width
|| aAttr
== nsGkAtoms::height
) {
1964 // Make sure we store the <window> attributes as outer window size, see
1965 // bug 1444525 & co.
1966 ConvertWindowSize(this, aAttr
, ConversionDirection::InnerToOuter
,
1967 maybeConvertedValue
);
1971 mLocalStore
= do_GetService("@mozilla.org/xul/xulstore;1");
1972 if (NS_WARN_IF(!mLocalStore
)) {
1973 return NS_ERROR_NOT_INITIALIZED
;
1977 return mLocalStore
->SetValue(
1978 uri
, windowElementId
, nsDependentAtomString(aAttr
), maybeConvertedValue
);
1981 void AppWindow::MaybeSavePersistentPositionAndSize(
1982 PersistentAttributes aAttributes
, Element
& aRootElement
,
1983 const nsAString
& aPersistString
, bool aShouldPersist
) {
1984 if ((aAttributes
& PersistentAttributes
{PersistentAttribute::Position
,
1985 PersistentAttribute::Size
})
1990 // get our size, position and mode to persist
1991 LayoutDeviceIntRect rect
;
1992 if (NS_FAILED(mWindow
->GetRestoredBounds(rect
))) {
1996 // we use CSS pixels for size, but desktop pixels for position
1997 CSSToLayoutDeviceScale sizeScale
= UnscaledDevicePixelsPerCSSPixel();
1998 DesktopToLayoutDeviceScale posScale
= DevicePixelsPerDesktopPixel();
2000 // make our position relative to our parent, if any
2001 nsCOMPtr
<nsIBaseWindow
> parent(do_QueryReferent(mParentWindow
));
2003 int32_t parentX
, parentY
;
2004 if (NS_SUCCEEDED(parent
->GetPosition(&parentX
, &parentY
))) {
2005 rect
.MoveBy(-parentX
, -parentY
);
2009 nsAutoString sizeString
;
2010 // (only for size elements which are persisted)
2011 if (aAttributes
.contains(PersistentAttribute::Position
)) {
2012 if (aPersistString
.Find(u
"screenX") >= 0) {
2013 sizeString
.Truncate();
2014 sizeString
.AppendInt(NSToIntRound(rect
.X() / posScale
.scale
));
2015 aRootElement
.SetAttr(nsGkAtoms::screenX
, sizeString
, IgnoreErrors());
2016 if (aShouldPersist
) {
2017 Unused
<< SetPersistentValue(nsGkAtoms::screenX
, sizeString
);
2020 if (aPersistString
.Find(u
"screenY") >= 0) {
2021 sizeString
.Truncate();
2022 sizeString
.AppendInt(NSToIntRound(rect
.Y() / posScale
.scale
));
2023 aRootElement
.SetAttr(nsGkAtoms::screenY
, sizeString
, IgnoreErrors());
2024 if (aShouldPersist
) {
2025 Unused
<< SetPersistentValue(nsGkAtoms::screenY
, sizeString
);
2030 if (aAttributes
.contains(PersistentAttribute::Size
)) {
2031 LayoutDeviceIntRect innerRect
=
2032 rect
- GetOuterToInnerSizeDifference(mWindow
);
2033 if (aPersistString
.Find(u
"width") >= 0) {
2034 sizeString
.Truncate();
2035 sizeString
.AppendInt(NSToIntRound(innerRect
.Width() / sizeScale
.scale
));
2036 aRootElement
.SetAttr(nsGkAtoms::width
, sizeString
, IgnoreErrors());
2037 if (aShouldPersist
) {
2038 Unused
<< SetPersistentValue(nsGkAtoms::width
, sizeString
);
2041 if (aPersistString
.Find(u
"height") >= 0) {
2042 sizeString
.Truncate();
2043 sizeString
.AppendInt(NSToIntRound(innerRect
.Height() / sizeScale
.scale
));
2044 aRootElement
.SetAttr(nsGkAtoms::height
, sizeString
, IgnoreErrors());
2045 if (aShouldPersist
) {
2046 Unused
<< SetPersistentValue(nsGkAtoms::height
, sizeString
);
2051 Unused
<< MaybeSaveEarlyWindowPersistentValues(rect
);
2054 void AppWindow::MaybeSavePersistentMiscAttributes(
2055 PersistentAttributes aAttributes
, Element
& aRootElement
,
2056 const nsAString
& aPersistString
, bool aShouldPersist
) {
2057 if (!aAttributes
.contains(PersistentAttribute::Misc
)) {
2061 nsSizeMode sizeMode
= mWindow
->SizeMode();
2062 nsAutoString sizeString
;
2063 if (sizeMode
!= nsSizeMode_Minimized
) {
2064 if (sizeMode
== nsSizeMode_Maximized
) {
2065 sizeString
.Assign(SIZEMODE_MAXIMIZED
);
2066 } else if (sizeMode
== nsSizeMode_Fullscreen
) {
2067 sizeString
.Assign(SIZEMODE_FULLSCREEN
);
2069 sizeString
.Assign(SIZEMODE_NORMAL
);
2071 aRootElement
.SetAttr(nsGkAtoms::sizemode
, sizeString
, IgnoreErrors());
2072 if (aShouldPersist
&& aPersistString
.Find(u
"sizemode") >= 0) {
2073 Unused
<< SetPersistentValue(nsGkAtoms::sizemode
, sizeString
);
2076 aRootElement
.SetAttribute(u
"gtktiledwindow"_ns
,
2077 mWindow
->IsTiled() ? u
"true"_ns
: u
"false"_ns
,
2079 if (aPersistString
.Find(u
"zlevel") >= 0) {
2081 nsCOMPtr
<nsIWindowMediator
> mediator(
2082 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
2084 mediator
->GetZLevel(this, &zLevel
);
2085 sizeString
.Truncate();
2086 sizeString
.AppendInt(zLevel
);
2087 aRootElement
.SetAttr(nsGkAtoms::zlevel
, sizeString
, IgnoreErrors());
2088 if (aShouldPersist
) {
2089 Unused
<< SetPersistentValue(nsGkAtoms::zlevel
, sizeString
);
2095 void AppWindow::SavePersistentAttributes(
2096 const PersistentAttributes aAttributes
) {
2097 // can happen when the persistence timer fires at an inopportune time
2098 // during window shutdown
2103 nsCOMPtr
<dom::Element
> docShellElement
= GetWindowDOMElement();
2104 if (!docShellElement
) {
2108 nsAutoString persistString
;
2109 docShellElement
->GetAttr(nsGkAtoms::persist
, persistString
);
2110 if (persistString
.IsEmpty()) { // quick check which sometimes helps
2111 mPersistentAttributesDirty
.clear();
2115 bool shouldPersist
= mWindow
->SizeMode() != nsSizeMode_Fullscreen
;
2116 MaybeSavePersistentPositionAndSize(aAttributes
, *docShellElement
,
2117 persistString
, shouldPersist
);
2118 MaybeSavePersistentMiscAttributes(aAttributes
, *docShellElement
,
2119 persistString
, shouldPersist
);
2120 mPersistentAttributesDirty
-= aAttributes
;
2123 NS_IMETHODIMP
AppWindow::GetWindowDOMWindow(mozIDOMWindowProxy
** aDOMWindow
) {
2124 NS_ENSURE_STATE(mDocShell
);
2126 if (!mDOMWindow
) mDOMWindow
= mDocShell
->GetWindow();
2127 NS_ENSURE_TRUE(mDOMWindow
, NS_ERROR_FAILURE
);
2129 *aDOMWindow
= mDOMWindow
;
2130 NS_ADDREF(*aDOMWindow
);
2134 dom::Element
* AppWindow::GetWindowDOMElement() const {
2135 NS_ENSURE_TRUE(mDocShell
, nullptr);
2137 nsCOMPtr
<nsIDocumentViewer
> viewer
;
2138 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
2139 NS_ENSURE_TRUE(viewer
, nullptr);
2141 const dom::Document
* document
= viewer
->GetDocument();
2142 NS_ENSURE_TRUE(document
, nullptr);
2144 return document
->GetRootElement();
2147 nsresult
AppWindow::ContentShellAdded(nsIDocShellTreeItem
* aContentShell
,
2149 // Set the default content tree owner
2151 NS_ENSURE_SUCCESS(EnsurePrimaryContentTreeOwner(), NS_ERROR_FAILURE
);
2152 aContentShell
->SetTreeOwner(mPrimaryContentTreeOwner
);
2153 mPrimaryContentShell
= aContentShell
;
2154 mPrimaryBrowserParent
= nullptr;
2156 NS_ENSURE_SUCCESS(EnsureContentTreeOwner(), NS_ERROR_FAILURE
);
2157 aContentShell
->SetTreeOwner(mContentTreeOwner
);
2158 if (mPrimaryContentShell
== aContentShell
) mPrimaryContentShell
= nullptr;
2164 nsresult
AppWindow::ContentShellRemoved(nsIDocShellTreeItem
* aContentShell
) {
2165 if (mPrimaryContentShell
== aContentShell
) {
2166 mPrimaryContentShell
= nullptr;
2172 AppWindow::GetPrimaryContentSize(int32_t* aWidth
, int32_t* aHeight
) {
2173 if (mPrimaryBrowserParent
) {
2174 return GetPrimaryRemoteTabSize(aWidth
, aHeight
);
2176 if (mPrimaryContentShell
) {
2177 return GetPrimaryContentShellSize(aWidth
, aHeight
);
2179 return NS_ERROR_UNEXPECTED
;
2182 nsresult
AppWindow::GetPrimaryRemoteTabSize(int32_t* aWidth
, int32_t* aHeight
) {
2183 BrowserHost
* host
= BrowserHost::GetFrom(mPrimaryBrowserParent
.get());
2184 // Need strong ref, since Client* can run script.
2185 RefPtr
<dom::Element
> element
= host
->GetOwnerElement();
2186 NS_ENSURE_STATE(element
);
2188 CSSIntSize
size(element
->ClientWidth(), element
->ClientHeight());
2189 LayoutDeviceIntSize sizeDev
=
2190 RoundedToInt(size
* UnscaledDevicePixelsPerCSSPixel());
2192 *aWidth
= sizeDev
.width
;
2195 *aHeight
= sizeDev
.height
;
2200 nsresult
AppWindow::GetPrimaryContentShellSize(int32_t* aWidth
,
2202 NS_ENSURE_STATE(mPrimaryContentShell
);
2204 nsCOMPtr
<nsIBaseWindow
> shellWindow(do_QueryInterface(mPrimaryContentShell
));
2205 NS_ENSURE_STATE(shellWindow
);
2207 LayoutDeviceIntSize sizeDev
= shellWindow
->GetSize();
2209 *aWidth
= sizeDev
.width
;
2212 *aHeight
= sizeDev
.height
;
2218 AppWindow::SetPrimaryContentSize(int32_t aWidth
, int32_t aHeight
) {
2219 if (mPrimaryBrowserParent
) {
2220 return SetPrimaryRemoteTabSize(aWidth
, aHeight
);
2222 if (mPrimaryContentShell
) {
2223 return SizeShellTo(mPrimaryContentShell
, aWidth
, aHeight
);
2225 return NS_ERROR_UNEXPECTED
;
2228 nsresult
AppWindow::SetPrimaryRemoteTabSize(int32_t aWidth
, int32_t aHeight
) {
2229 int32_t shellWidth
, shellHeight
;
2230 GetPrimaryRemoteTabSize(&shellWidth
, &shellHeight
);
2231 SizeShellToWithLimit(aWidth
, aHeight
, shellWidth
, shellHeight
);
2235 nsresult
AppWindow::GetRootShellSize(int32_t* aWidth
, int32_t* aHeight
) {
2236 NS_ENSURE_TRUE(mDocShell
, NS_ERROR_FAILURE
);
2237 return mDocShell
->GetSize(aWidth
, aHeight
);
2240 nsresult
AppWindow::SetRootShellSize(int32_t aWidth
, int32_t aHeight
) {
2241 return SizeShellTo(mDocShell
, aWidth
, aHeight
);
2244 NS_IMETHODIMP
AppWindow::SizeShellTo(nsIDocShellTreeItem
* aShellItem
,
2245 int32_t aCX
, int32_t aCY
) {
2246 MOZ_ASSERT(aShellItem
== mDocShell
|| aShellItem
== mPrimaryContentShell
);
2247 if (aShellItem
== mDocShell
) {
2249 LayoutDeviceIntSize(aCX
, aCY
) + GetOuterToInnerSizeDifference(mWindow
);
2250 SetSize(newSize
.width
, newSize
.height
, /* aRepaint = */ true);
2251 mDominantClientSize
= true;
2255 // XXXTAB This is wrong, we should actually reflow based on the passed in
2256 // shell. For now we are hacking and doing delta sizing. This is bad
2257 // because it assumes all size we add will go to the shell which probably
2259 nsCOMPtr
<nsIBaseWindow
> shellAsWin(do_QueryInterface(aShellItem
));
2260 NS_ENSURE_TRUE(shellAsWin
, NS_ERROR_FAILURE
);
2264 shellAsWin
->GetSize(&width
, &height
);
2266 SizeShellToWithLimit(aCX
, aCY
, width
, height
);
2271 NS_IMETHODIMP
AppWindow::ExitModalLoop(nsresult aStatus
) {
2272 if (mContinueModalLoop
) EnableParent(true);
2273 mContinueModalLoop
= false;
2274 mModalStatus
= aStatus
;
2278 // top-level function to create a new window
2279 NS_IMETHODIMP
AppWindow::CreateNewWindow(int32_t aChromeFlags
,
2280 nsIOpenWindowInfo
* aOpenWindowInfo
,
2281 nsIAppWindow
** _retval
) {
2282 NS_ENSURE_ARG_POINTER(_retval
);
2284 if (aChromeFlags
& nsIWebBrowserChrome::CHROME_OPENAS_CHROME
) {
2287 "Unexpected nsOpenWindowInfo when creating a new chrome window");
2288 return CreateNewChromeWindow(aChromeFlags
, _retval
);
2291 return CreateNewContentWindow(aChromeFlags
, aOpenWindowInfo
, _retval
);
2294 NS_IMETHODIMP
AppWindow::CreateNewChromeWindow(int32_t aChromeFlags
,
2295 nsIAppWindow
** _retval
) {
2296 nsCOMPtr
<nsIAppShellService
> appShell(
2297 do_GetService(NS_APPSHELLSERVICE_CONTRACTID
));
2298 NS_ENSURE_TRUE(appShell
, NS_ERROR_FAILURE
);
2300 // Just do a normal create of a window and return.
2301 nsCOMPtr
<nsIAppWindow
> newWindow
;
2302 appShell
->CreateTopLevelWindow(
2303 this, nullptr, aChromeFlags
, nsIAppShellService::SIZE_TO_CONTENT
,
2304 nsIAppShellService::SIZE_TO_CONTENT
, getter_AddRefs(newWindow
));
2306 NS_ENSURE_TRUE(newWindow
, NS_ERROR_FAILURE
);
2308 newWindow
.forget(_retval
);
2313 NS_IMETHODIMP
AppWindow::CreateNewContentWindow(
2314 int32_t aChromeFlags
, nsIOpenWindowInfo
* aOpenWindowInfo
,
2315 nsIAppWindow
** _retval
) {
2316 nsCOMPtr
<nsIAppShellService
> appShell(
2317 do_GetService(NS_APPSHELLSERVICE_CONTRACTID
));
2318 NS_ENSURE_TRUE(appShell
, NS_ERROR_FAILURE
);
2320 // We need to create a new top level window and then enter a nested
2321 // loop. Eventually the new window will be told that it has loaded,
2322 // at which time we know it is safe to spin out of the nested loop
2323 // and allow the opening code to proceed.
2325 nsCOMPtr
<nsIURI
> uri
;
2326 nsAutoCString urlStr
;
2327 urlStr
.AssignLiteral(BROWSER_CHROME_URL_QUOTED
);
2329 nsCOMPtr
<nsIIOService
> service(do_GetService(NS_IOSERVICE_CONTRACTID
));
2331 service
->NewURI(urlStr
, nullptr, nullptr, getter_AddRefs(uri
));
2333 NS_ENSURE_TRUE(uri
, NS_ERROR_FAILURE
);
2335 // We need to create a chrome window to contain the content window we're about
2336 // to pass back. The subject principal needs to be system while we're creating
2337 // it to make things work right, so force a system caller. See bug 799348
2338 // comment 13 for a description of what happens when we don't.
2339 nsCOMPtr
<nsIAppWindow
> newWindow
;
2341 AutoNoJSAPI nojsapi
;
2342 appShell
->CreateTopLevelWindow(this, uri
, aChromeFlags
, 615, 480,
2343 getter_AddRefs(newWindow
));
2344 NS_ENSURE_TRUE(newWindow
, NS_ERROR_FAILURE
);
2348 static_cast<AppWindow
*>(static_cast<nsIAppWindow
*>(newWindow
));
2350 // Specify which flags should be used by browser.xhtml to create the initial
2351 // content browser window.
2352 appWin
->mInitialOpenWindowInfo
= aOpenWindowInfo
;
2354 // Specify that we want the window to remain locked until the chrome has
2356 appWin
->LockUntilChromeLoad();
2359 AutoNoJSAPI nojsapi
;
2360 SpinEventLoopUntil("AppWindow::CreateNewContentWindow"_ns
,
2361 [&]() { return !appWin
->IsLocked(); });
2364 NS_ENSURE_STATE(appWin
->mPrimaryContentShell
||
2365 appWin
->mPrimaryBrowserParent
);
2366 MOZ_ASSERT_IF(appWin
->mPrimaryContentShell
,
2367 !aOpenWindowInfo
->GetNextRemoteBrowser());
2369 newWindow
.forget(_retval
);
2374 NS_IMETHODIMP
AppWindow::GetHasPrimaryContent(bool* aResult
) {
2375 *aResult
= mPrimaryBrowserParent
|| mPrimaryContentShell
;
2379 void AppWindow::EnableParent(bool aEnable
) {
2380 nsCOMPtr
<nsIBaseWindow
> parentWindow
;
2381 nsCOMPtr
<nsIWidget
> parentWidget
;
2383 parentWindow
= do_QueryReferent(mParentWindow
);
2384 if (parentWindow
) parentWindow
->GetMainWidget(getter_AddRefs(parentWidget
));
2385 if (parentWidget
) parentWidget
->Enable(aEnable
);
2388 // Constrain the window to its proper z-level
2389 bool AppWindow::ConstrainToZLevel(bool aImmediate
, nsWindowZ
* aPlacement
,
2390 nsIWidget
* aReqBelow
,
2391 nsIWidget
** aActualBelow
) {
2393 /* Do we have a parent window? This means our z-order is already constrained,
2394 since we're a dependent window. Our window list isn't hierarchical,
2395 so we can't properly calculate placement for such a window.
2396 Should we just abort? */
2397 nsCOMPtr
<nsIBaseWindow
> parentWindow
= do_QueryReferent(mParentWindow
);
2402 nsCOMPtr
<nsIWindowMediator
> mediator(
2403 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
2404 if (!mediator
) return false;
2407 uint32_t position
, newPosition
, zLevel
;
2408 nsIAppWindow
* us
= this;
2411 mediator
->GetZLevel(this, &zLevel
);
2413 // translate from WidgetGUIEvent to nsIWindowMediator constants
2414 position
= nsIWindowMediator::zLevelTop
;
2415 if (*aPlacement
== nsWindowZBottom
|| zLevel
== nsIAppWindow::lowestZ
)
2416 position
= nsIWindowMediator::zLevelBottom
;
2417 else if (*aPlacement
== nsWindowZRelative
)
2418 position
= nsIWindowMediator::zLevelBelow
;
2420 if (NS_SUCCEEDED(mediator
->CalculateZPosition(
2421 us
, position
, aReqBelow
, &newPosition
, aActualBelow
, &altered
))) {
2422 /* If we were asked to move to the top but constrained to remain
2423 below one of our other windows, first move all windows in that
2424 window's layer and above to the top. This allows the user to
2425 click a window which can't be topmost and still bring mozilla
2426 to the foreground. */
2428 (position
== nsIWindowMediator::zLevelTop
||
2429 (position
== nsIWindowMediator::zLevelBelow
&& aReqBelow
== 0)))
2430 PlaceWindowLayersBehind(zLevel
+ 1, nsIAppWindow::highestZ
, 0);
2432 if (*aPlacement
!= nsWindowZBottom
&&
2433 position
== nsIWindowMediator::zLevelBottom
)
2435 if (altered
|| aImmediate
) {
2436 if (newPosition
== nsIWindowMediator::zLevelTop
)
2437 *aPlacement
= nsWindowZTop
;
2438 else if (newPosition
== nsIWindowMediator::zLevelBottom
)
2439 *aPlacement
= nsWindowZBottom
;
2441 *aPlacement
= nsWindowZRelative
;
2444 nsCOMPtr
<nsIBaseWindow
> ourBase
= do_QueryObject(this);
2446 nsCOMPtr
<nsIWidget
> ourWidget
;
2447 ourBase
->GetMainWidget(getter_AddRefs(ourWidget
));
2448 ourWidget
->PlaceBehind(*aPlacement
== nsWindowZBottom
2451 *aActualBelow
, false);
2456 /* CalculateZPosition can tell us to be below nothing, because it tries
2457 not to change something it doesn't recognize. A request to verify
2458 being below an unrecognized window, then, is treated as a request
2459 to come to the top (below null) */
2460 nsCOMPtr
<nsIAppWindow
> windowAbove
;
2461 if (newPosition
== nsIWindowMediator::zLevelBelow
&& *aActualBelow
) {
2462 windowAbove
= (*aActualBelow
)->GetWidgetListener()->GetAppWindow();
2465 mediator
->SetZPosition(us
, newPosition
, windowAbove
);
2471 /* Re-z-position all windows in the layers from aLowLevel to aHighLevel,
2472 inclusive, to be behind aBehind. aBehind of null means on top.
2473 Note this method actually does nothing to our relative window positions.
2474 (And therefore there's no need to inform WindowMediator we're moving
2475 things, because we aren't.) This method is useful for, say, moving
2476 a range of layers of our own windows relative to windows belonging to
2477 external applications.
2479 void AppWindow::PlaceWindowLayersBehind(uint32_t aLowLevel
, uint32_t aHighLevel
,
2480 nsIAppWindow
* aBehind
) {
2481 // step through windows in z-order from top to bottommost window
2483 nsCOMPtr
<nsIWindowMediator
> mediator(
2484 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
));
2485 if (!mediator
) return;
2487 nsCOMPtr
<nsISimpleEnumerator
> windowEnumerator
;
2488 mediator
->GetZOrderAppWindowEnumerator(0, true,
2489 getter_AddRefs(windowEnumerator
));
2490 if (!windowEnumerator
) return;
2492 // each window will be moved behind previousHighWidget, itself
2493 // a moving target. initialize it.
2494 nsCOMPtr
<nsIWidget
> previousHighWidget
;
2496 nsCOMPtr
<nsIBaseWindow
> highBase(do_QueryInterface(aBehind
));
2497 if (highBase
) highBase
->GetMainWidget(getter_AddRefs(previousHighWidget
));
2500 // get next lower window
2502 while (NS_SUCCEEDED(windowEnumerator
->HasMoreElements(&more
)) && more
) {
2503 uint32_t nextZ
; // z-level of nextWindow
2504 nsCOMPtr
<nsISupports
> nextWindow
;
2505 windowEnumerator
->GetNext(getter_AddRefs(nextWindow
));
2506 nsCOMPtr
<nsIAppWindow
> nextAppWindow(do_QueryInterface(nextWindow
));
2507 nextAppWindow
->GetZLevel(&nextZ
);
2508 if (nextZ
< aLowLevel
)
2509 break; // we've processed all windows through aLowLevel
2511 // move it just below its next higher window
2512 nsCOMPtr
<nsIBaseWindow
> nextBase(do_QueryInterface(nextAppWindow
));
2514 nsCOMPtr
<nsIWidget
> nextWidget
;
2515 nextBase
->GetMainWidget(getter_AddRefs(nextWidget
));
2516 if (nextZ
<= aHighLevel
)
2517 nextWidget
->PlaceBehind(eZPlacementBelow
, previousHighWidget
, false);
2518 previousHighWidget
= nextWidget
;
2523 void AppWindow::SetContentScrollbarVisibility(bool aVisible
) {
2524 nsCOMPtr
<nsPIDOMWindowOuter
> contentWin(
2525 do_GetInterface(mPrimaryContentShell
));
2530 nsContentUtils::SetScrollbarsVisibility(contentWin
->GetDocShell(), aVisible
);
2533 void AppWindow::ApplyChromeFlags() {
2534 nsCOMPtr
<dom::Element
> window
= GetWindowDOMElement();
2539 if (mChromeLoaded
) {
2540 // The two calls in this block don't need to happen early because they
2541 // don't cause a global restyle on the document. Not only that, but the
2542 // scrollbar stuff needs a content area to toggle the scrollbars on anyway.
2543 // So just don't do these until mChromeLoaded is true.
2545 // Scrollbars have their own special treatment.
2546 SetContentScrollbarVisibility(mChromeFlags
&
2547 nsIWebBrowserChrome::CHROME_SCROLLBARS
);
2550 /* the other flags are handled together. we have style rules
2551 in navigator.css that trigger visibility based on
2552 the 'chromehidden' attribute of the <window> tag. */
2553 nsAutoString newvalue
;
2555 if (!(mChromeFlags
& nsIWebBrowserChrome::CHROME_MENUBAR
))
2556 newvalue
.AppendLiteral("menubar ");
2558 if (!(mChromeFlags
& nsIWebBrowserChrome::CHROME_TOOLBAR
))
2559 newvalue
.AppendLiteral("toolbar ");
2561 if (!(mChromeFlags
& nsIWebBrowserChrome::CHROME_LOCATIONBAR
))
2562 newvalue
.AppendLiteral("location ");
2564 if (!(mChromeFlags
& nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR
))
2565 newvalue
.AppendLiteral("directories ");
2567 if (!(mChromeFlags
& nsIWebBrowserChrome::CHROME_STATUSBAR
))
2568 newvalue
.AppendLiteral("status ");
2570 if (!(mChromeFlags
& nsIWebBrowserChrome::CHROME_EXTRA
))
2571 newvalue
.AppendLiteral("extrachrome ");
2573 // Note that if we're not actually changing the value this will be a no-op,
2574 // so no need to compare to the old value.
2575 IgnoredErrorResult rv
;
2576 window
->SetAttribute(u
"chromehidden"_ns
, newvalue
, rv
);
2580 AppWindow::BeforeStartLayout() {
2582 // Ordering here is important, loading width/height values in
2583 // LoadPersistentWindowState() depends on the chromemargin attribute (since
2584 // we need to translate outer to inner sizes).
2585 SyncAttributesToWidget();
2586 LoadPersistentWindowState();
2594 AppWindow::LockAspectRatio(bool aShouldLock
) {
2595 mWindow
->LockAspectRatio(aShouldLock
);
2600 AppWindow::NeedFastSnaphot() {
2601 MOZ_ASSERT(mWindow
);
2603 return NS_ERROR_FAILURE
;
2605 mWindow
->SetNeedFastSnaphot();
2609 void AppWindow::LoadPersistentWindowState() {
2610 nsCOMPtr
<dom::Element
> docShellElement
= GetWindowDOMElement();
2611 if (!docShellElement
) {
2615 // Check if the window wants to persist anything.
2616 nsAutoString persist
;
2617 docShellElement
->GetAttr(nsGkAtoms::persist
, persist
);
2618 if (persist
.IsEmpty()) {
2622 auto loadValue
= [&](nsAtom
* aAttr
) {
2623 nsDependentAtomString
attrString(aAttr
);
2624 if (persist
.Find(attrString
) >= 0) {
2626 nsresult rv
= GetPersistentValue(aAttr
, value
);
2627 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Failed to get persistent state.");
2628 if (NS_SUCCEEDED(rv
) && !value
.IsEmpty()) {
2629 docShellElement
->SetAttr(aAttr
, value
, IgnoreErrors());
2634 loadValue(nsGkAtoms::screenX
);
2635 loadValue(nsGkAtoms::screenY
);
2636 loadValue(nsGkAtoms::width
);
2637 loadValue(nsGkAtoms::height
);
2638 loadValue(nsGkAtoms::sizemode
);
2641 void AppWindow::IntrinsicallySizeShell(const CSSIntSize
& aWindowDiff
,
2642 int32_t& aSpecWidth
,
2643 int32_t& aSpecHeight
) {
2644 nsCOMPtr
<nsIDocumentViewer
> viewer
;
2645 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
2649 RefPtr
<nsDocShell
> docShell
= mDocShell
;
2651 CSSIntCoord maxWidth
= 0;
2652 CSSIntCoord maxHeight
= 0;
2653 CSSIntCoord prefWidth
= 0;
2654 if (RefPtr element
= GetWindowDOMElement()) {
2655 nsAutoString prefWidthAttr
;
2656 if (element
->GetAttr(nsGkAtoms::prefwidth
, prefWidthAttr
)) {
2657 // TODO: Make this more generic perhaps?
2658 if (prefWidthAttr
.EqualsLiteral("min-width")) {
2659 if (auto* f
= element
->GetPrimaryFrame(FlushType::Frames
)) {
2660 const auto& coord
= f
->StylePosition()->mMinWidth
;
2661 if (coord
.ConvertsToLength()) {
2662 prefWidth
= CSSPixel::FromAppUnitsRounded(coord
.ToLength());
2669 Maybe
<CSSIntSize
> size
=
2670 viewer
->GetContentSize(maxWidth
, maxHeight
, prefWidth
);
2674 nsPresContext
* pc
= viewer
->GetPresContext();
2675 MOZ_ASSERT(pc
, "Should have pres context");
2677 int32_t width
= pc
->CSSPixelsToDevPixels(size
->width
);
2678 int32_t height
= pc
->CSSPixelsToDevPixels(size
->height
);
2679 SizeShellTo(docShell
, width
, height
);
2681 // Update specified size for the final LoadPositionFromXUL call.
2682 aSpecWidth
= size
->width
+ aWindowDiff
.width
;
2683 aSpecHeight
= size
->height
+ aWindowDiff
.height
;
2686 void AppWindow::SizeShell() {
2687 AutoRestore
<bool> sizingShellFromXUL(mSizingShellFromXUL
);
2688 mSizingShellFromXUL
= true;
2690 int32_t specWidth
= -1, specHeight
= -1;
2691 bool gotSize
= false;
2693 nsAutoString windowType
;
2694 if (nsCOMPtr
<dom::Element
> windowElement
= GetWindowDOMElement()) {
2695 windowElement
->GetAttr(nsGkAtoms::windowtype
, windowType
);
2698 const CSSIntSize windowDiff
= GetOuterToInnerSizeDifferenceInCSSPixels(
2699 mWindow
, UnscaledDevicePixelsPerCSSPixel());
2701 // If we're using fingerprint resistance, we're going to resize the window
2702 // once we have primary content.
2703 if (nsContentUtils::ShouldResistFingerprinting(
2704 "if RFP is enabled we want to round the dimensions of the new"
2705 "new pop up window regardless of their origin",
2706 RFPTarget::RoundWindowSize
) &&
2707 windowType
.EqualsLiteral("navigator:browser")) {
2708 // Once we've got primary content, force dimensions.
2709 if (mPrimaryContentShell
|| mPrimaryBrowserParent
) {
2710 ForceRoundedDimensions();
2712 // Always avoid setting size/sizemode on this window.
2713 mIgnoreXULSize
= true;
2714 mIgnoreXULSizeMode
= true;
2715 } else if (!mIgnoreXULSize
) {
2716 gotSize
= LoadSizeFromXUL(specWidth
, specHeight
);
2717 specWidth
+= windowDiff
.width
;
2718 specHeight
+= windowDiff
.height
;
2721 bool positionSet
= !mIgnoreXULPosition
;
2722 nsCOMPtr
<nsIAppWindow
> parentWindow(do_QueryReferent(mParentWindow
));
2723 #if defined(XP_UNIX) && !defined(XP_MACOSX)
2724 // don't override WM placement on unix for independent, top-level windows
2725 // (however, we think the benefits of intelligent dependent window placement
2726 // trump that override.)
2727 if (!parentWindow
) positionSet
= false;
2730 // We have to do this before sizing the window, because sizing depends
2731 // on the resolution of the screen we're on. But positioning needs to
2732 // know the size so that it can constrain to screen bounds.... as an
2733 // initial guess here, we'll use the specified size (if any).
2734 positionSet
= LoadPositionFromXUL(specWidth
, specHeight
);
2738 SetSpecifiedSize(specWidth
, specHeight
);
2741 // If LoadSizeFromXUL set the size, mIntrinsicallySized will be false.
2742 if (mIntrinsicallySized
) {
2743 IntrinsicallySizeShell(windowDiff
, specWidth
, specHeight
);
2746 // Now that we have set the window's final size, we can re-do its
2747 // positioning so that it is properly constrained to the screen.
2749 LoadPositionFromXUL(specWidth
, specHeight
);
2752 UpdateWindowStateFromMiscXULAttributes();
2754 if (mChromeLoaded
&& mCenterAfterLoad
&& !positionSet
&&
2755 mWindow
->SizeMode() == nsSizeMode_Normal
) {
2756 Center(parentWindow
, parentWindow
? false : true, false);
2760 NS_IMETHODIMP
AppWindow::GetXULBrowserWindow(
2761 nsIXULBrowserWindow
** aXULBrowserWindow
) {
2762 NS_IF_ADDREF(*aXULBrowserWindow
= mXULBrowserWindow
);
2766 NS_IMETHODIMP
AppWindow::SetXULBrowserWindow(
2767 nsIXULBrowserWindow
* aXULBrowserWindow
) {
2768 mXULBrowserWindow
= aXULBrowserWindow
;
2772 // Given the dimensions of some content area held within this XUL window, and
2773 // assuming that that content area will change its dimensions in linear
2774 // proportion to the dimensions of this XUL window, changes the size of the XUL
2775 // window so that the content area reaches a particular size.
2776 void AppWindow::SizeShellToWithLimit(int32_t aDesiredWidth
,
2777 int32_t aDesiredHeight
,
2778 int32_t shellItemWidth
,
2779 int32_t shellItemHeight
) {
2780 int32_t widthDelta
= aDesiredWidth
- shellItemWidth
;
2781 int32_t heightDelta
= aDesiredHeight
- shellItemHeight
;
2783 int32_t winWidth
= 0;
2784 int32_t winHeight
= 0;
2786 GetSize(&winWidth
, &winHeight
);
2787 // There's no point in trying to make the window smaller than the
2788 // desired content area size --- that's not likely to work. This whole
2789 // function assumes that the outer docshell is adding some constant
2790 // "border" chrome to the content area.
2791 winWidth
= std::max(winWidth
+ widthDelta
, aDesiredWidth
);
2792 winHeight
= std::max(winHeight
+ heightDelta
, aDesiredHeight
);
2794 // Note: Because of the asynchronous resizing on Linux we have to call
2795 // SetSize even when the size doesn't appear to change. A previous call that
2796 // has yet to complete can still change the size. We want the latest call to
2797 // define the final size.
2798 SetSize(winWidth
, winHeight
, true);
2799 mDominantClientSize
= true;
2802 nsresult
AppWindow::GetTabCount(uint32_t* aResult
) {
2803 if (mXULBrowserWindow
) {
2804 return mXULBrowserWindow
->GetTabCount(aResult
);
2811 nsresult
AppWindow::GetInitialOpenWindowInfo(
2812 nsIOpenWindowInfo
** aOpenWindowInfo
) {
2813 NS_ENSURE_ARG_POINTER(aOpenWindowInfo
);
2814 *aOpenWindowInfo
= do_AddRef(mInitialOpenWindowInfo
).take();
2818 PresShell
* AppWindow::GetPresShell() {
2822 return mDocShell
->GetPresShell();
2825 bool AppWindow::WindowMoved(nsIWidget
* aWidget
, int32_t x
, int32_t y
) {
2826 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
2828 nsCOMPtr
<nsPIDOMWindowOuter
> window
=
2829 mDocShell
? mDocShell
->GetWindow() : nullptr;
2830 pm
->AdjustPopupsOnWindowChange(window
);
2833 // Notify all tabs that the widget moved.
2834 if (mDocShell
&& mDocShell
->GetWindow()) {
2835 nsCOMPtr
<EventTarget
> eventTarget
=
2836 mDocShell
->GetWindow()->GetTopWindowRoot();
2837 nsContentUtils::DispatchChromeEvent(
2838 mDocShell
->GetDocument(), eventTarget
, u
"MozUpdateWindowPos"_ns
,
2839 CanBubble::eNo
, Cancelable::eNo
, nullptr);
2842 // Persist position, but not immediately, in case this OS is firing
2843 // repeated move events as the user drags the window
2844 PersistentAttributesDirty(PersistentAttribute::Position
, Async
);
2848 bool AppWindow::WindowResized(nsIWidget
* aWidget
, int32_t aWidth
,
2850 mDominantClientSize
= false;
2852 mDocShell
->SetPositionAndSize(0, 0, aWidth
, aHeight
, 0);
2854 // Persist size, but not immediately, in case this OS is firing
2855 // repeated size events as the user drags the sizing handle
2857 PersistentAttributesDirty(AllPersistentAttributes(), Async
);
2859 // Check if we need to continue a fullscreen change.
2860 switch (mFullscreenChangeState
) {
2861 case FullscreenChangeState::WillChange
:
2862 mFullscreenChangeState
= FullscreenChangeState::WidgetResized
;
2864 case FullscreenChangeState::WidgetEnteredFullscreen
:
2865 FinishFullscreenChange(true);
2867 case FullscreenChangeState::WidgetExitedFullscreen
:
2868 FinishFullscreenChange(false);
2870 case FullscreenChangeState::WidgetResized
:
2871 case FullscreenChangeState::NotChanging
:
2877 bool AppWindow::RequestWindowClose(nsIWidget
* aWidget
) {
2878 // Maintain a reference to this as it is about to get destroyed.
2879 nsCOMPtr
<nsIAppWindow
> appWindow(this);
2881 nsCOMPtr
<nsPIDOMWindowOuter
> window(mDocShell
? mDocShell
->GetWindow()
2883 nsCOMPtr
<EventTarget
> eventTarget
= do_QueryInterface(window
);
2885 RefPtr
<PresShell
> presShell
= mDocShell
->GetPresShell();
2887 mozilla::DebugOnly
<bool> dying
;
2888 MOZ_ASSERT(NS_SUCCEEDED(mDocShell
->IsBeingDestroyed(&dying
)) && dying
,
2889 "No presShell, but window is not being destroyed");
2890 } else if (eventTarget
) {
2891 RefPtr
<nsPresContext
> presContext
= presShell
->GetPresContext();
2893 nsEventStatus status
= nsEventStatus_eIgnore
;
2894 WidgetMouseEvent
event(true, eClose
, nullptr, WidgetMouseEvent::eReal
);
2895 if (NS_SUCCEEDED(EventDispatcher::Dispatch(eventTarget
, presContext
, &event
,
2896 nullptr, &status
)) &&
2897 status
== nsEventStatus_eConsumeNoDefault
)
2905 void AppWindow::SizeModeChanged(nsSizeMode aSizeMode
) {
2906 const bool wasWidgetInFullscreen
= mIsWidgetInFullscreen
;
2907 // Fullscreen and minimized states are usually compatible, and the widget
2908 // typically returns to fullscreen after restoration. By not updating the
2909 // widget's fullscreen state while it is minimized, we can avoid unnecessary
2910 // fullscreen exits, such as those encountered in bug 1823284.
2911 if (aSizeMode
!= nsSizeMode_Minimized
) {
2912 mIsWidgetInFullscreen
= aSizeMode
== nsSizeMode_Fullscreen
;
2915 const bool fullscreenChanged
= wasWidgetInFullscreen
!= mIsWidgetInFullscreen
;
2916 if (fullscreenChanged
) {
2917 FullscreenWillChange(mIsWidgetInFullscreen
);
2920 // An alwaysRaised (or higher) window will hide any newly opened normal
2921 // browser windows, so here we just drop a raised window to the normal
2922 // zlevel if it's maximized. We make no provision for automatically
2923 // re-raising it when restored.
2924 if (aSizeMode
== nsSizeMode_Maximized
|| aSizeMode
== nsSizeMode_Fullscreen
) {
2927 if (zLevel
> nsIAppWindow::normalZ
) {
2928 SetZLevel(nsIAppWindow::normalZ
);
2932 RecomputeBrowsingContextVisibility();
2934 PersistentAttributesDirty(PersistentAttribute::Misc
, Sync
);
2935 nsCOMPtr
<nsPIDOMWindowOuter
> ourWindow
=
2936 mDocShell
? mDocShell
->GetWindow() : nullptr;
2938 // Always fire a user-defined sizemodechange event on the window
2939 ourWindow
->DispatchCustomEvent(u
"sizemodechange"_ns
);
2942 if (PresShell
* presShell
= GetPresShell()) {
2943 presShell
->GetPresContext()->SizeModeChanged(aSizeMode
);
2946 if (fullscreenChanged
) {
2947 FullscreenChanged(mIsWidgetInFullscreen
);
2950 // Note the current implementation of SetSizeMode just stores
2951 // the new state; it doesn't actually resize. So here we store
2952 // the state and pass the event on to the OS. The day is coming
2953 // when we'll handle the event here, and the return result will
2954 // then need to be different.
2957 void AppWindow::UIResolutionChanged() {
2958 nsCOMPtr
<nsPIDOMWindowOuter
> ourWindow
=
2959 mDocShell
? mDocShell
->GetWindow() : nullptr;
2961 ourWindow
->DispatchCustomEvent(u
"resolutionchange"_ns
,
2962 ChromeOnlyDispatch::eYes
);
2966 void AppWindow::FullscreenWillChange(bool aInFullscreen
) {
2968 if (nsCOMPtr
<nsPIDOMWindowOuter
> ourWindow
= mDocShell
->GetWindow()) {
2969 ourWindow
->FullscreenWillChange(aInFullscreen
);
2972 MOZ_ASSERT(mFullscreenChangeState
== FullscreenChangeState::NotChanging
);
2974 CSSToLayoutDeviceScale scale
= UnscaledDevicePixelsPerCSSPixel();
2975 CSSIntSize windowSizeCSS
= RoundedToInt(GetSize() / scale
);
2977 CSSIntSize screenSizeCSS
;
2978 GetAvailScreenSize(&screenSizeCSS
.width
, &screenSizeCSS
.height
);
2980 // Check if the window is already at the expected dimensions. If it is, set
2981 // the fullscreen change state to WidgetResized to avoid waiting for a resize
2982 // event. On macOS, a fullscreen window could be slightly higher than
2983 // available screen size because of the OS menu bar isn't yet hidden.
2984 mFullscreenChangeState
=
2985 (aInFullscreen
== (windowSizeCSS
.width
== screenSizeCSS
.width
&&
2986 windowSizeCSS
.height
>= screenSizeCSS
.height
))
2987 ? FullscreenChangeState::WidgetResized
2988 : FullscreenChangeState::WillChange
;
2991 void AppWindow::FullscreenChanged(bool aInFullscreen
) {
2992 if (mFullscreenChangeState
== FullscreenChangeState::WidgetResized
) {
2993 FinishFullscreenChange(aInFullscreen
);
2995 NS_WARNING_ASSERTION(
2996 mFullscreenChangeState
== FullscreenChangeState::WillChange
,
2997 "Unexpected fullscreen change state");
2998 FullscreenChangeState newState
=
2999 aInFullscreen
? FullscreenChangeState::WidgetEnteredFullscreen
3000 : FullscreenChangeState::WidgetExitedFullscreen
;
3001 mFullscreenChangeState
= newState
;
3002 nsCOMPtr
<nsIAppWindow
> kungFuDeathGrip(this);
3003 // Wait for resize for a small amount of time.
3004 // 80ms is actually picked arbitrarily. But it shouldn't be too large
3005 // in case the widget resize is not going to happen at all, which can
3006 // be the case for some Linux window managers and possibly Android.
3007 NS_DelayedDispatchToCurrentThread(
3008 NS_NewRunnableFunction(
3009 "AppWindow::FullscreenChanged",
3010 [this, kungFuDeathGrip
, newState
, aInFullscreen
]() {
3011 if (mFullscreenChangeState
== newState
) {
3012 FinishFullscreenChange(aInFullscreen
);
3019 void AppWindow::FinishFullscreenChange(bool aInFullscreen
) {
3020 mFullscreenChangeState
= FullscreenChangeState::NotChanging
;
3022 if (nsCOMPtr
<nsPIDOMWindowOuter
> ourWindow
= mDocShell
->GetWindow()) {
3023 ourWindow
->FinishFullscreenChange(aInFullscreen
);
3028 void AppWindow::MacFullscreenMenubarOverlapChanged(
3029 mozilla::DesktopCoord aOverlapAmount
) {
3031 if (nsCOMPtr
<nsPIDOMWindowOuter
> ourWindow
= mDocShell
->GetWindow()) {
3032 ourWindow
->MacFullscreenMenubarOverlapChanged(aOverlapAmount
);
3037 void AppWindow::RecomputeBrowsingContextVisibility() {
3041 RefPtr bc
= mDocShell
->GetBrowsingContext();
3045 bc
->Canonical()->RecomputeAppWindowVisibility();
3048 void AppWindow::OcclusionStateChanged(bool aIsFullyOccluded
) {
3052 RecomputeBrowsingContextVisibility();
3053 if (RefPtr win
= mDocShell
->GetWindow()) {
3054 // And always fire a user-defined occlusionstatechange event on the window
3055 win
->DispatchCustomEvent(u
"occlusionstatechange"_ns
,
3056 ChromeOnlyDispatch::eYes
);
3060 void AppWindow::OSToolbarButtonPressed() {
3061 // Keep a reference as setting the chrome flags can fire events.
3062 nsCOMPtr
<nsIAppWindow
> appWindow(this);
3064 // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
3065 // due to components with multiple sidebar components
3066 // (such as Mail/News, Addressbook, etc)... and frankly,
3067 // Mac IE, OmniWeb, and other Mac OS X apps all work this way
3068 uint32_t chromeMask
= (nsIWebBrowserChrome::CHROME_TOOLBAR
|
3069 nsIWebBrowserChrome::CHROME_LOCATIONBAR
|
3070 nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR
);
3072 nsCOMPtr
<nsIWebBrowserChrome
> wbc(do_GetInterface(appWindow
));
3075 uint32_t chromeFlags
, newChromeFlags
= 0;
3076 wbc
->GetChromeFlags(&chromeFlags
);
3077 newChromeFlags
= chromeFlags
& chromeMask
;
3078 if (!newChromeFlags
)
3079 chromeFlags
|= chromeMask
;
3081 chromeFlags
&= (~newChromeFlags
);
3082 wbc
->SetChromeFlags(chromeFlags
);
3085 bool AppWindow::ZLevelChanged(bool aImmediate
, nsWindowZ
* aPlacement
,
3086 nsIWidget
* aRequestBelow
,
3087 nsIWidget
** aActualBelow
) {
3088 if (aActualBelow
) *aActualBelow
= nullptr;
3090 return ConstrainToZLevel(aImmediate
, aPlacement
, aRequestBelow
, aActualBelow
);
3093 void AppWindow::WindowActivated() {
3094 nsCOMPtr
<nsIAppWindow
> appWindow(this);
3096 // focusing the window could cause it to close, so keep a reference to it
3098 if (nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocShell
->GetWindow()) {
3099 if (RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager()) {
3100 fm
->WindowRaised(window
, nsFocusManager::GenerateFocusActionId());
3105 if (mChromeLoaded
) {
3106 PersistentAttributesDirty(AllPersistentAttributes(), Sync
);
3110 void AppWindow::WindowDeactivated() {
3112 if (nsCOMPtr
<nsPIDOMWindowOuter
> window
= mDocShell
->GetWindow()) {
3113 if (RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager()) {
3114 if (!fm
->IsTestMode()) {
3115 fm
->WindowLowered(window
, nsFocusManager::GenerateFocusActionId());
3122 #ifdef USE_NATIVE_MENUS
3124 struct LoadNativeMenusListener
{
3125 LoadNativeMenusListener(Document
* aDoc
, nsIWidget
* aParentWindow
)
3126 : mDocument(aDoc
), mParentWindow(aParentWindow
) {}
3128 RefPtr
<Document
> mDocument
;
3129 nsCOMPtr
<nsIWidget
> mParentWindow
;
3132 // On macOS the hidden window is created eagerly, and we want to wait for it to
3133 // load the native menus.
3134 static bool sWaitingForHiddenWindowToLoadNativeMenus
=
3142 static nsTArray
<LoadNativeMenusListener
> sLoadNativeMenusListeners
;
3144 static void BeginLoadNativeMenus(Document
* aDoc
, nsIWidget
* aParentWindow
);
3146 static void LoadNativeMenus(Document
* aDoc
, nsIWidget
* aParentWindow
) {
3147 MOZ_ASSERT(!gfxPlatform::IsHeadless());
3149 // Find the menubar tag (if there is more than one, we ignore all but
3151 nsCOMPtr
<nsINodeList
> menubarElements
= aDoc
->GetElementsByTagNameNS(
3152 u
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns
,
3155 RefPtr
<Element
> menubar
;
3156 if (menubarElements
) {
3157 menubar
= Element::FromNodeOrNull(menubarElements
->Item(0));
3160 widget::NativeMenuSupport::CreateNativeMenuBar(aParentWindow
, menubar
);
3162 if (sWaitingForHiddenWindowToLoadNativeMenus
) {
3163 sWaitingForHiddenWindowToLoadNativeMenus
= false;
3164 for (auto& listener
: sLoadNativeMenusListeners
) {
3165 BeginLoadNativeMenus(listener
.mDocument
, listener
.mParentWindow
);
3167 sLoadNativeMenusListeners
.Clear();
3171 class L10nReadyPromiseHandler final
: public dom::PromiseNativeHandler
{
3175 L10nReadyPromiseHandler(Document
* aDoc
, nsIWidget
* aParentWindow
)
3176 : mDocument(aDoc
), mWindow(aParentWindow
) {}
3178 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
3179 ErrorResult
& aRv
) override
{
3180 LoadNativeMenus(mDocument
, mWindow
);
3183 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
3184 ErrorResult
& aRv
) override
{
3185 // Again, this shouldn't happen, but fallback to loading the menus as is.
3187 "L10nReadyPromiseHandler rejected - loading fallback native "
3189 LoadNativeMenus(mDocument
, mWindow
);
3193 ~L10nReadyPromiseHandler() = default;
3195 RefPtr
<Document
> mDocument
;
3196 nsCOMPtr
<nsIWidget
> mWindow
;
3199 NS_IMPL_ISUPPORTS0(L10nReadyPromiseHandler
)
3201 static void BeginLoadNativeMenus(Document
* aDoc
, nsIWidget
* aParentWindow
) {
3202 if (RefPtr
<DocumentL10n
> l10n
= aDoc
->GetL10n()) {
3203 // Wait for l10n to be ready so the menus are localized.
3204 RefPtr
<Promise
> promise
= l10n
->Ready();
3205 MOZ_ASSERT(promise
);
3206 RefPtr handler
= new L10nReadyPromiseHandler(aDoc
, aParentWindow
);
3207 promise
->AppendNativeHandler(handler
);
3209 // Something went wrong loading the doc and l10n wasn't created. This
3210 // shouldn't really happen, but if it does fallback to trying to load
3212 LoadNativeMenus(aDoc
, aParentWindow
);
3218 class AppWindowTimerCallback final
: public nsITimerCallback
, public nsINamed
{
3220 explicit AppWindowTimerCallback(AppWindow
* aWindow
) : mWindow(aWindow
) {}
3222 NS_DECL_THREADSAFE_ISUPPORTS
3224 NS_IMETHOD
Notify(nsITimer
* aTimer
) override
{
3225 // Although this object participates in a refcount cycle (this -> mWindow
3226 // -> mSPTimer -> this), mSPTimer is a one-shot timer and releases this
3227 // after it fires. So we don't need to release mWindow here.
3229 mWindow
->FirePersistenceTimer();
3233 NS_IMETHOD
GetName(nsACString
& aName
) override
{
3234 aName
.AssignLiteral("AppWindowTimerCallback");
3239 ~AppWindowTimerCallback() {}
3241 RefPtr
<AppWindow
> mWindow
;
3244 NS_IMPL_ISUPPORTS(AppWindowTimerCallback
, nsITimerCallback
, nsINamed
)
3246 void AppWindow::PersistentAttributesDirty(PersistentAttributes aAttributes
,
3247 PersistentAttributeUpdate aUpdate
) {
3248 aAttributes
= aAttributes
& mPersistentAttributesMask
;
3249 if (aAttributes
.isEmpty()) {
3253 mPersistentAttributesDirty
+= aAttributes
;
3254 if (aUpdate
== Sync
) {
3255 // Only apply the attributes we've been requested to apply sync, not other
3256 // potentially dirty attributes that have been requested asynchronously.
3257 SavePersistentAttributes(aAttributes
);
3261 mSPTimer
= NS_NewTimer();
3263 NS_WARNING("Couldn't create timer instance?");
3268 RefPtr
<AppWindowTimerCallback
> callback
= new AppWindowTimerCallback(this);
3269 mSPTimer
->InitWithCallback(callback
, SIZE_PERSISTENCE_TIMEOUT
,
3270 nsITimer::TYPE_ONE_SHOT
);
3273 void AppWindow::FirePersistenceTimer() { SavePersistentAttributes(); }
3275 //----------------------------------------
3276 // nsIWebProgessListener implementation
3277 //----------------------------------------
3279 AppWindow::OnProgressChange(nsIWebProgress
* aProgress
, nsIRequest
* aRequest
,
3280 int32_t aCurSelfProgress
, int32_t aMaxSelfProgress
,
3281 int32_t aCurTotalProgress
,
3282 int32_t aMaxTotalProgress
) {
3283 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3288 AppWindow::OnStateChange(nsIWebProgress
* aProgress
, nsIRequest
* aRequest
,
3289 uint32_t aStateFlags
, nsresult aStatus
) {
3290 // If the notification is not about a document finishing, then just
3292 if (!(aStateFlags
& nsIWebProgressListener::STATE_STOP
) ||
3293 !(aStateFlags
& nsIWebProgressListener::STATE_IS_NETWORK
)) {
3297 if (mChromeLoaded
) return NS_OK
;
3299 // If this document notification is for a frame then ignore it...
3300 nsCOMPtr
<mozIDOMWindowProxy
> eventWin
;
3301 aProgress
->GetDOMWindow(getter_AddRefs(eventWin
));
3302 auto* eventPWin
= nsPIDOMWindowOuter::From(eventWin
);
3304 nsPIDOMWindowOuter
* rootPWin
= eventPWin
->GetPrivateRoot();
3305 if (eventPWin
!= rootPWin
) return NS_OK
;
3308 mChromeLoaded
= true;
3309 mLockedUntilChromeLoad
= false;
3311 #ifdef USE_NATIVE_MENUS
3312 ///////////////////////////////
3313 // Find the Menubar DOM and Load the menus, hooking them up to the loaded
3315 ///////////////////////////////
3316 if (!gfxPlatform::IsHeadless()) {
3317 if (RefPtr
<Document
> menubarDoc
= mDocShell
->GetExtantDocument()) {
3318 if (mIsHiddenWindow
|| !sWaitingForHiddenWindowToLoadNativeMenus
) {
3319 BeginLoadNativeMenus(menubarDoc
, mWindow
);
3321 sLoadNativeMenusListeners
.EmplaceBack(menubarDoc
, mWindow
);
3325 #endif // USE_NATIVE_MENUS
3333 AppWindow::OnLocationChange(nsIWebProgress
* aProgress
, nsIRequest
* aRequest
,
3334 nsIURI
* aURI
, uint32_t aFlags
) {
3335 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3340 AppWindow::OnStatusChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
3341 nsresult aStatus
, const char16_t
* aMessage
) {
3342 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3347 AppWindow::OnSecurityChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
3349 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3354 AppWindow::OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
3355 nsIRequest
* aRequest
, uint32_t aEvent
) {
3356 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3361 * ExecuteCloseHandler - Run the close handler, if any.
3362 * @return true iff we found a close handler to run.
3364 bool AppWindow::ExecuteCloseHandler() {
3365 /* If the event handler closes this window -- a likely scenario --
3366 things get deleted out of order without this death grip.
3367 (The problem may be the death grip in nsWindow::windowProc,
3368 which forces this window's widget to remain alive longer
3369 than it otherwise would.) */
3370 nsCOMPtr
<nsIAppWindow
> kungFuDeathGrip(this);
3372 nsCOMPtr
<EventTarget
> eventTarget
;
3374 eventTarget
= do_QueryInterface(mDocShell
->GetWindow());
3378 nsCOMPtr
<nsIDocumentViewer
> viewer
;
3379 mDocShell
->GetDocViewer(getter_AddRefs(viewer
));
3381 RefPtr
<nsPresContext
> presContext
= viewer
->GetPresContext();
3383 nsEventStatus status
= nsEventStatus_eIgnore
;
3384 WidgetMouseEvent
event(true, eClose
, nullptr, WidgetMouseEvent::eReal
);
3386 nsresult rv
= EventDispatcher::Dispatch(eventTarget
, presContext
, &event
,
3388 if (NS_SUCCEEDED(rv
) && status
== nsEventStatus_eConsumeNoDefault
)
3390 // else fall through and return false
3395 } // ExecuteCloseHandler
3397 void AppWindow::ConstrainToOpenerScreen(int32_t* aX
, int32_t* aY
) {
3398 if (mOpenerScreenRect
.IsEmpty()) {
3403 int32_t left
, top
, width
, height
;
3404 // Constrain initial positions to the same screen as opener
3405 nsCOMPtr
<nsIScreenManager
> screenmgr
=
3406 do_GetService("@mozilla.org/gfx/screenmanager;1");
3408 nsCOMPtr
<nsIScreen
> screen
= screenmgr
->ScreenForRect(mOpenerScreenRect
);
3410 screen
->GetAvailRectDisplayPix(&left
, &top
, &width
, &height
);
3411 if (*aX
< left
|| *aX
> left
+ width
) {
3414 if (*aY
< top
|| *aY
> top
+ height
) {
3421 nsIAppWindow
* AppWindow::WidgetListenerDelegate::GetAppWindow() {
3422 return mAppWindow
->GetAppWindow();
3425 PresShell
* AppWindow::WidgetListenerDelegate::GetPresShell() {
3426 return mAppWindow
->GetPresShell();
3429 bool AppWindow::WidgetListenerDelegate::WindowMoved(nsIWidget
* aWidget
,
3430 int32_t aX
, int32_t aY
,
3432 RefPtr
<AppWindow
> holder
= mAppWindow
;
3433 return holder
->WindowMoved(aWidget
, aX
, aY
);
3436 bool AppWindow::WidgetListenerDelegate::WindowResized(nsIWidget
* aWidget
,
3439 RefPtr
<AppWindow
> holder
= mAppWindow
;
3440 return holder
->WindowResized(aWidget
, aWidth
, aHeight
);
3443 bool AppWindow::WidgetListenerDelegate::RequestWindowClose(nsIWidget
* aWidget
) {
3444 RefPtr
<AppWindow
> holder
= mAppWindow
;
3445 return holder
->RequestWindowClose(aWidget
);
3448 void AppWindow::WidgetListenerDelegate::SizeModeChanged(nsSizeMode aSizeMode
) {
3449 RefPtr
<AppWindow
> holder
= mAppWindow
;
3450 holder
->SizeModeChanged(aSizeMode
);
3453 void AppWindow::WidgetListenerDelegate::UIResolutionChanged() {
3454 RefPtr
<AppWindow
> holder
= mAppWindow
;
3455 holder
->UIResolutionChanged();
3458 void AppWindow::WidgetListenerDelegate::MacFullscreenMenubarOverlapChanged(
3459 DesktopCoord aOverlapAmount
) {
3460 RefPtr
<AppWindow
> holder
= mAppWindow
;
3461 return holder
->MacFullscreenMenubarOverlapChanged(aOverlapAmount
);
3464 void AppWindow::WidgetListenerDelegate::OcclusionStateChanged(
3465 bool aIsFullyOccluded
) {
3466 RefPtr
<AppWindow
> holder
= mAppWindow
;
3467 holder
->OcclusionStateChanged(aIsFullyOccluded
);
3470 void AppWindow::WidgetListenerDelegate::OSToolbarButtonPressed() {
3471 RefPtr
<AppWindow
> holder
= mAppWindow
;
3472 holder
->OSToolbarButtonPressed();
3475 bool AppWindow::WidgetListenerDelegate::ZLevelChanged(
3476 bool aImmediate
, nsWindowZ
* aPlacement
, nsIWidget
* aRequestBelow
,
3477 nsIWidget
** aActualBelow
) {
3478 RefPtr
<AppWindow
> holder
= mAppWindow
;
3479 return holder
->ZLevelChanged(aImmediate
, aPlacement
, aRequestBelow
,
3483 void AppWindow::WidgetListenerDelegate::WindowActivated() {
3484 RefPtr
<AppWindow
> holder
= mAppWindow
;
3485 holder
->WindowActivated();
3488 void AppWindow::WidgetListenerDelegate::WindowDeactivated() {
3489 RefPtr
<AppWindow
> holder
= mAppWindow
;
3490 holder
->WindowDeactivated();
3493 } // namespace mozilla