Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / xpfe / appshell / AppWindow.cpp
blob8281f5483a172a44dbb1666463d16092c293c261
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/. */
7 #include "ErrorList.h"
8 #include "mozilla/MathAlgorithms.h"
10 // Local includes
11 #include "AppWindow.h"
12 #include <algorithm>
14 // Helper classes
15 #include "nsPrintfCString.h"
16 #include "nsString.h"
17 #include "nsWidgetsCID.h"
18 #include "nsThreadUtils.h"
19 #include "nsNetCID.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"
33 #include "nsScreen.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"
43 #include "nsIURI.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"
56 #include "prenv.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"
74 #ifdef XP_WIN
75 # include "mozilla/PreXULSkeletonUI.h"
76 # include "mozilla/WindowsVersion.h"
77 # include "nsIWindowsUIUtils.h"
78 #endif
80 #include "mozilla/dom/DocumentL10n.h"
82 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
83 # include "mozilla/widget/NativeMenuSupport.h"
84 # define USE_NATIVE_MENUS
85 #endif
87 #define SIZEMODE_NORMAL u"normal"_ns
88 #define SIZEMODE_MAXIMIZED u"maximized"_ns
89 #define SIZEMODE_MINIMIZED u"minimized"_ns
90 #define SIZEMODE_FULLSCREEN u"fullscreen"_ns
92 #define SIZE_PERSISTENCE_TIMEOUT 500 // msec
94 //*****************************************************************************
95 //*** AppWindow: Object Management
96 //*****************************************************************************
98 namespace mozilla {
100 using dom::AutoNoJSAPI;
101 using dom::BrowserHost;
102 using dom::BrowsingContext;
103 using dom::Document;
104 using dom::DocumentL10n;
105 using dom::Element;
106 using dom::EventTarget;
107 using dom::LoadURIOptions;
108 using dom::Promise;
110 AppWindow::AppWindow(uint32_t aChromeFlags)
111 : mChromeTreeOwner(nullptr),
112 mContentTreeOwner(nullptr),
113 mPrimaryContentTreeOwner(nullptr),
114 mModalStatus(NS_OK),
115 mFullscreenChangeState(FullscreenChangeState::NotChanging),
116 mContinueModalLoop(false),
117 mDebuting(false),
118 mChromeLoaded(false),
119 mSizingShellFromXUL(false),
120 mShowAfterLoad(false),
121 mIntrinsicallySized(false),
122 mCenterAfterLoad(false),
123 mIsHiddenWindow(false),
124 mLockedUntilChromeLoad(false),
125 mIgnoreXULSize(false),
126 mIgnoreXULPosition(false),
127 mChromeFlagsFrozen(false),
128 mIgnoreXULSizeMode(false),
129 mDestroying(false),
130 mRegistered(false),
131 mDominantClientSize(false),
132 mChromeFlags(aChromeFlags),
133 mWidgetListenerDelegate(this) {}
135 AppWindow::~AppWindow() {
136 if (mSPTimer) {
137 mSPTimer->Cancel();
138 mSPTimer = nullptr;
140 Destroy();
143 //*****************************************************************************
144 // AppWindow::nsISupports
145 //*****************************************************************************
147 NS_IMPL_ADDREF(AppWindow)
148 NS_IMPL_RELEASE(AppWindow)
150 NS_INTERFACE_MAP_BEGIN(AppWindow)
151 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAppWindow)
152 NS_INTERFACE_MAP_ENTRY(nsIAppWindow)
153 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
154 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
155 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
156 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
157 NS_INTERFACE_MAP_ENTRY_CONCRETE(AppWindow)
158 NS_INTERFACE_MAP_END
160 nsresult AppWindow::Initialize(nsIAppWindow* aParent, nsIAppWindow* aOpener,
161 int32_t aInitialWidth, int32_t aInitialHeight,
162 bool aIsHiddenWindow,
163 widget::InitData& widgetInitData) {
164 nsresult rv;
165 nsCOMPtr<nsIWidget> parentWidget;
167 mIsHiddenWindow = aIsHiddenWindow;
169 DesktopIntPoint initialPos;
170 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aOpener));
171 if (base) {
172 LayoutDeviceIntRect rect = base->GetPositionAndSize();
173 mOpenerScreenRect =
174 DesktopIntRect::Round(rect / base->DevicePixelsPerDesktopPixel());
175 if (!mOpenerScreenRect.IsEmpty()) {
176 initialPos = mOpenerScreenRect.TopLeft();
177 ConstrainToOpenerScreen(&initialPos.x.value, &initialPos.y.value);
181 // XXX: need to get the default window size from prefs...
182 // Doesn't come from prefs... will come from CSS/XUL/RDF
183 DesktopIntRect deskRect(initialPos,
184 DesktopIntSize(aInitialWidth, aInitialHeight));
186 // Create top level window
187 if (gfxPlatform::IsHeadless()) {
188 mWindow = nsIWidget::CreateHeadlessWidget();
189 } else {
190 mWindow = nsIWidget::CreateTopLevelWindow();
192 if (!mWindow) {
193 return NS_ERROR_FAILURE;
196 /* This next bit is troublesome. We carry two different versions of a pointer
197 to our parent window. One is the parent window's widget, which is passed
198 to our own widget. The other is a weak reference we keep here to our
199 parent AppWindow. The former is useful to the widget, and we can't
200 trust its treatment of the parent reference because they're platform-
201 specific. The latter is useful to this class.
202 A better implementation would be one in which the parent keeps strong
203 references to its children and closes them before it allows itself
204 to be closed. This would mimic the behaviour of OSes that support
205 top-level child windows in OSes that do not. Later.
207 nsCOMPtr<nsIBaseWindow> parentAsWin(do_QueryInterface(aParent));
208 if (parentAsWin) {
209 parentAsWin->GetMainWidget(getter_AddRefs(parentWidget));
210 mParentWindow = do_GetWeakReference(aParent);
213 mWindow->SetWidgetListener(&mWidgetListenerDelegate);
214 rv = mWindow->Create(parentWidget.get(), // Parent nsIWidget
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. Previously important on
221 // Windows, but no longer has any effect there.
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
226 // nsWindowWatcher.
227 RefPtr<BrowsingContext> browsingContext =
228 BrowsingContext::CreateIndependent(BrowsingContext::Type::Chrome);
230 // Create web shell
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);
240 r.MoveTo(0, 0);
241 NS_ENSURE_SUCCESS(
242 mDocShell->InitWindow(mWindow, r.X(), r.Y(), r.Width(), r.Height()),
243 NS_ERROR_FAILURE);
245 // Attach a WebProgress listener.during initialization...
246 mDocShell->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK);
248 mWindow->MaybeDispatchInitialFocusEvent();
250 return rv;
253 //*****************************************************************************
254 // AppWindow::nsIIntefaceRequestor
255 //*****************************************************************************
257 NS_IMETHODIMP AppWindow::GetInterface(const nsIID& aIID, void** aSink) {
258 nsresult rv;
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);
280 return rv;
282 if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome)) &&
283 NS_SUCCEEDED(EnsureContentTreeOwner()) &&
284 NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink))) {
285 return NS_OK;
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);
300 return NS_OK;
303 NS_IMETHODIMP AppWindow::GetChromeFlags(uint32_t* aChromeFlags) {
304 NS_ENSURE_ARG_POINTER(aChromeFlags);
305 *aChromeFlags = mChromeFlags;
306 return NS_OK;
309 NS_IMETHODIMP AppWindow::SetChromeFlags(uint32_t aChromeFlags) {
310 NS_ASSERTION(!mChromeFlagsFrozen,
311 "SetChromeFlags() after AssumeChromeFlagsAreFrozen()!");
313 mChromeFlags = aChromeFlags;
314 if (mChromeLoaded) {
315 ApplyChromeFlags();
317 return NS_OK;
320 NS_IMETHODIMP AppWindow::AssumeChromeFlagsAreFrozen() {
321 mChromeFlagsFrozen = true;
322 return NS_OK;
325 NS_IMETHODIMP AppWindow::SetIntrinsicallySized(bool aIntrinsicallySized) {
326 mIntrinsicallySized = aIntrinsicallySized;
327 return NS_OK;
330 NS_IMETHODIMP AppWindow::GetIntrinsicallySized(bool* aIntrinsicallySized) {
331 NS_ENSURE_ARG_POINTER(aIntrinsicallySized);
333 *aIntrinsicallySized = mIntrinsicallySized;
334 return NS_OK;
337 NS_IMETHODIMP AppWindow::GetPrimaryContentShell(
338 nsIDocShellTreeItem** aDocShellTreeItem) {
339 NS_ENSURE_ARG_POINTER(aDocShellTreeItem);
340 NS_IF_ADDREF(*aDocShellTreeItem = mPrimaryContentShell);
341 return NS_OK;
344 NS_IMETHODIMP
345 AppWindow::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
346 if (aPrimary) {
347 mPrimaryBrowserParent = aTab;
348 mPrimaryContentShell = nullptr;
349 } else if (mPrimaryBrowserParent == aTab) {
350 mPrimaryBrowserParent = nullptr;
353 return NS_OK;
356 NS_IMETHODIMP
357 AppWindow::RemoteTabRemoved(nsIRemoteTab* aTab) {
358 if (aTab == mPrimaryBrowserParent) {
359 mPrimaryBrowserParent = nullptr;
362 return NS_OK;
365 NS_IMETHODIMP
366 AppWindow::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
367 nsCOMPtr<nsIRemoteTab> tab = mPrimaryBrowserParent;
368 tab.forget(aTab);
369 return NS_OK;
372 NS_IMETHODIMP
373 AppWindow::GetPrimaryContentBrowsingContext(
374 mozilla::dom::BrowsingContext** aBc) {
375 if (mPrimaryBrowserParent) {
376 return mPrimaryBrowserParent->GetBrowsingContext(aBc);
378 if (mPrimaryContentShell) {
379 return mPrimaryContentShell->GetBrowsingContextXPCOM(aBc);
381 *aBc = nullptr;
382 return NS_OK;
385 static LayoutDeviceIntSize GetOuterToInnerSizeDifference(nsIWidget* aWindow) {
386 if (!aWindow) {
387 return LayoutDeviceIntSize();
389 return aWindow->ClientToWindowSizeDifference();
392 static CSSIntSize GetOuterToInnerSizeDifferenceInCSSPixels(
393 nsIWidget* aWindow, CSSToLayoutDeviceScale aScale) {
394 LayoutDeviceIntSize devPixelSize = GetOuterToInnerSizeDifference(aWindow);
395 return RoundedToInt(devPixelSize / aScale);
398 NS_IMETHODIMP
399 AppWindow::GetOuterToInnerHeightDifferenceInCSSPixels(uint32_t* aResult) {
400 *aResult = GetOuterToInnerSizeDifferenceInCSSPixels(
401 mWindow, UnscaledDevicePixelsPerCSSPixel())
402 .height;
403 return NS_OK;
406 NS_IMETHODIMP
407 AppWindow::GetOuterToInnerWidthDifferenceInCSSPixels(uint32_t* aResult) {
408 *aResult = GetOuterToInnerSizeDifferenceInCSSPixels(
409 mWindow, UnscaledDevicePixelsPerCSSPixel())
410 .width;
411 return NS_OK;
414 nsTArray<RefPtr<mozilla::LiveResizeListener>>
415 AppWindow::GetLiveResizeListeners() {
416 nsTArray<RefPtr<mozilla::LiveResizeListener>> listeners;
417 if (mPrimaryBrowserParent) {
418 BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
419 RefPtr<mozilla::LiveResizeListener> actor = host->GetActor();
420 if (actor) {
421 listeners.AppendElement(actor);
424 return listeners;
427 NS_IMETHODIMP AppWindow::ShowModal() {
428 AUTO_PROFILER_LABEL("AppWindow::ShowModal", OTHER);
430 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
431 MOZ_ASSERT_UNREACHABLE(
432 "Trying to show modal window after shutdown started.");
433 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
436 // Store locally so it doesn't die on us
437 nsCOMPtr<nsIWidget> window = mWindow;
438 nsCOMPtr<nsIAppWindow> tempRef = this;
440 #ifdef USE_NATIVE_MENUS
441 if (!gfxPlatform::IsHeadless()) {
442 // On macOS, for modals created early in startup. (e.g.
443 // ProfileManager/ProfileDowngrade) this creates a fallback menu for the
444 // menu bar which only contains a "Quit" menu item. This allows the user to
445 // quit the application in a regular way with cmd+Q.
446 widget::NativeMenuSupport::CreateNativeMenuBar(mWindow, nullptr);
448 #endif
450 window->SetModal(true);
451 mContinueModalLoop = true;
452 EnableParent(false);
455 AutoNoJSAPI nojsapi;
456 SpinEventLoopUntil("AppWindow::ShowModal"_ns, [&]() {
457 if (MOZ_UNLIKELY(
458 AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed))) {
459 // TODO: Bug 1699041 would apply also here: Should we return an error
460 // if we are bailing out from a pre-existing modal dialog for shutdown?
461 ExitModalLoop(NS_OK);
463 return !mContinueModalLoop;
467 mContinueModalLoop = false;
468 window->SetModal(false);
469 /* Note there's no EnableParent(true) here to match the false one
470 above. That's done in ExitModalLoop. It's important that the parent
471 be re-enabled before this window is made invisible; to do otherwise
472 causes bizarre z-ordering problems. At this point, the window is
473 already invisible.
474 No known current implementation of Enable would have a problem with
475 re-enabling the parent twice, so we could do it again here without
476 breaking any current implementation. But that's unnecessary if the
477 modal loop is always exited using ExitModalLoop (the other way would be
478 to change the protected member variable directly.)
481 return mModalStatus;
484 NS_IMETHODIMP AppWindow::RollupAllPopups() {
485 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
486 pm->Rollup({});
488 return NS_OK;
491 //*****************************************************************************
492 // AppWindow::nsIBaseWindow
493 //*****************************************************************************
495 NS_IMETHODIMP AppWindow::InitWindow(nsIWidget* parentWidget, int32_t x,
496 int32_t y, int32_t cx, int32_t cy) {
497 // XXX First Check In
498 NS_ASSERTION(false, "Not Yet Implemented");
499 return NS_OK;
502 NS_IMETHODIMP AppWindow::Destroy() {
503 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
505 if (mDocShell) {
506 mDocShell->RemoveProgressListener(this);
509 if (mSPTimer) {
510 mSPTimer->Cancel();
511 SavePersistentAttributes();
512 mSPTimer = nullptr;
515 if (!mWindow) return NS_OK;
517 // Ensure we don't reenter this code
518 if (mDestroying) return NS_OK;
520 mozilla::AutoRestore<bool> guard(mDestroying);
521 mDestroying = true;
523 nsCOMPtr<nsIAppShellService> appShell(
524 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
525 NS_ASSERTION(appShell, "Couldn't get appShell... xpcom shutdown?");
526 if (appShell) {
527 appShell->UnregisterTopLevelWindow(static_cast<nsIAppWindow*>(this));
530 // Remove modality (if any) and hide while destroying. More than
531 // a convenience, the hide prevents user interaction with the partially
532 // destroyed window. This is especially necessary when the eldest window
533 // in a stack of modal windows is destroyed first. It happens.
534 ExitModalLoop(NS_OK);
535 // XXX: Skip unmapping the window on Linux due to GLX hangs on the compositor
536 // thread with NVIDIA driver 310.32. We don't need to worry about user
537 // interactions with destroyed windows on X11 either.
538 #ifndef MOZ_WIDGET_GTK
539 if (mWindow) mWindow->Show(false);
540 #endif
542 RemoveTooltipSupport();
544 mDOMWindow = nullptr;
545 if (mDocShell) {
546 RefPtr<BrowsingContext> bc(mDocShell->GetBrowsingContext());
547 mDocShell->Destroy();
548 bc->Detach();
549 mDocShell = nullptr; // this can cause reentrancy of this function
552 mPrimaryContentShell = nullptr;
554 if (mContentTreeOwner) {
555 mContentTreeOwner->AppWindow(nullptr);
556 NS_RELEASE(mContentTreeOwner);
558 if (mPrimaryContentTreeOwner) {
559 mPrimaryContentTreeOwner->AppWindow(nullptr);
560 NS_RELEASE(mPrimaryContentTreeOwner);
562 if (mChromeTreeOwner) {
563 mChromeTreeOwner->AppWindow(nullptr);
564 NS_RELEASE(mChromeTreeOwner);
566 if (mWindow) {
567 mWindow->SetWidgetListener(nullptr); // nsWebShellWindow hackery
568 mWindow->Destroy();
569 mWindow = nullptr;
572 if (!mIsHiddenWindow && mRegistered) {
573 /* Inform appstartup we've destroyed this window and it could
574 quit now if it wanted. This must happen at least after mDocShell
575 is destroyed, because onunload handlers fire then, and those being
576 script, anything could happen. A new window could open, even.
577 See bug 130719. */
578 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
579 NS_ASSERTION(obssvc, "Couldn't get observer service?");
581 if (obssvc)
582 obssvc->NotifyObservers(nullptr, "xul-window-destroyed", nullptr);
585 return NS_OK;
588 NS_IMETHODIMP AppWindow::GetDevicePixelsPerDesktopPixel(double* aScale) {
589 *aScale = mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0;
590 return NS_OK;
593 double AppWindow::GetWidgetCSSToDeviceScale() {
594 return mWindow ? mWindow->GetDefaultScale().scale : 1.0;
597 NS_IMETHODIMP AppWindow::SetPositionDesktopPix(int32_t aX, int32_t aY) {
598 return MoveResize(Some(DesktopIntPoint(aX, aY)), Nothing(), false);
601 // The parameters here are device pixels; do the best we can to convert to
602 // desktop px, using the window's current scale factor (if available).
603 NS_IMETHODIMP AppWindow::SetPosition(int32_t aX, int32_t aY) {
604 // Don't reset the window's size mode here - platforms that don't want to move
605 // maximized windows should reset it in their respective Move implementation.
606 return MoveResize(Some(LayoutDeviceIntPoint(aX, aY)), Nothing(), false);
609 NS_IMETHODIMP AppWindow::GetPosition(int32_t* aX, int32_t* aY) {
610 return GetPositionAndSize(aX, aY, nullptr, nullptr);
613 NS_IMETHODIMP AppWindow::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) {
614 /* any attempt to set the window's size or position overrides the window's
615 zoom state. this is important when these two states are competing while
616 the window is being opened. but it should probably just always be so. */
617 return MoveResize(Nothing(), Some(LayoutDeviceIntSize(aCX, aCY)), aRepaint);
620 NS_IMETHODIMP AppWindow::GetSize(int32_t* aCX, int32_t* aCY) {
621 return GetPositionAndSize(nullptr, nullptr, aCX, aCY);
624 NS_IMETHODIMP AppWindow::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
625 int32_t aCY, uint32_t aFlags) {
626 /* any attempt to set the window's size or position overrides the window's
627 zoom state. this is important when these two states are competing while
628 the window is being opened. but it should probably just always be so. */
629 return MoveResize(Some(LayoutDeviceIntPoint(aX, aY)),
630 Some(LayoutDeviceIntSize(aCX, aCY)),
631 !!(aFlags & nsIBaseWindow::eRepaint));
634 NS_IMETHODIMP AppWindow::GetPositionAndSize(int32_t* x, int32_t* y, int32_t* cx,
635 int32_t* cy) {
636 if (!mWindow) return NS_ERROR_FAILURE;
638 LayoutDeviceIntRect rect = mWindow->GetScreenBounds();
640 if (x) *x = rect.X();
641 if (y) *y = rect.Y();
642 if (cx) *cx = rect.Width();
643 if (cy) *cy = rect.Height();
645 return NS_OK;
648 NS_IMETHODIMP
649 AppWindow::SetDimensions(DimensionRequest&& aRequest) {
650 if (aRequest.mDimensionKind == DimensionKind::Inner) {
651 // For the chrome the inner size is the root shell size, and for the
652 // content it's the primary content size. We lack an indicator here that
653 // would allow us to distinguish between the two.
654 return NS_ERROR_NOT_IMPLEMENTED;
657 MOZ_TRY(aRequest.SupplementFrom(this));
658 return aRequest.ApplyOuterTo(this);
661 NS_IMETHODIMP
662 AppWindow::GetDimensions(DimensionKind aDimensionKind, int32_t* aX, int32_t* aY,
663 int32_t* aCX, int32_t* aCY) {
664 if (aDimensionKind == DimensionKind::Inner) {
665 // For the chrome the inner size is the root shell size, and for the
666 // content it's the primary content size. We lack an indicator here that
667 // would allow us to distinguish between the two.
668 return NS_ERROR_NOT_IMPLEMENTED;
670 return GetPositionAndSize(aX, aY, aCX, aCY);
673 nsresult AppWindow::MoveResize(const Maybe<LayoutDeviceIntPoint>& aPosition,
674 const Maybe<LayoutDeviceIntSize>& aSize,
675 bool aRepaint) {
676 NS_ENSURE_STATE(mWindow);
677 DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
678 return MoveResize(aPosition ? Some(*aPosition / scale) : Nothing(),
679 aSize ? Some(*aSize / scale) : Nothing(), aRepaint);
682 nsresult AppWindow::MoveResize(const Maybe<DesktopPoint>& aPosition,
683 const Maybe<DesktopSize>& aSize, bool aRepaint) {
684 NS_ENSURE_STATE(mWindow);
685 PersistentAttributes dirtyAttributes;
687 if (!aPosition && !aSize) {
688 MOZ_ASSERT_UNREACHABLE("Doing nothing?");
689 return NS_ERROR_UNEXPECTED;
692 if (aSize) {
693 mWindow->SetSizeMode(nsSizeMode_Normal);
694 mIntrinsicallySized = false;
695 mDominantClientSize = false;
698 if (aPosition && aSize) {
699 mWindow->Resize(aPosition->x, aPosition->y, aSize->width, aSize->height,
700 aRepaint);
701 dirtyAttributes = {PersistentAttribute::Size,
702 PersistentAttribute::Position};
703 } else if (aSize) {
704 mWindow->Resize(aSize->width, aSize->height, aRepaint);
705 dirtyAttributes = {PersistentAttribute::Size};
706 } else if (aPosition) {
707 mWindow->Move(aPosition->x, aPosition->y);
708 dirtyAttributes = {PersistentAttribute::Position};
711 if (mSizingShellFromXUL) {
712 // If we're invoked for sizing from XUL, we want to neither ignore anything
713 // nor persist anything, since it's already the value in XUL.
714 return NS_OK;
716 if (!mChromeLoaded) {
717 // If we're called before the chrome is loaded someone obviously wants this
718 // window at this size & in the normal size mode (since it is the only mode
719 // in which setting dimensions makes sense). We don't persist this one-time
720 // position/size.
721 if (aPosition) {
722 mIgnoreXULPosition = true;
724 if (aSize) {
725 mIgnoreXULSize = true;
726 mIgnoreXULSizeMode = true;
728 return NS_OK;
731 PersistentAttributesDirty(dirtyAttributes, Sync);
732 return NS_OK;
735 NS_IMETHODIMP AppWindow::Center(nsIAppWindow* aRelative, bool aScreen,
736 bool aAlert) {
737 DesktopIntRect rect;
738 bool screenCoordinates = false, windowCoordinates = false;
739 nsresult result;
741 if (!mChromeLoaded) {
742 // note we lose the parameters. at time of writing, this isn't a problem.
743 mCenterAfterLoad = true;
744 return NS_OK;
747 if (!aScreen && !aRelative) {
748 return NS_ERROR_INVALID_ARG;
751 nsCOMPtr<nsIScreenManager> screenmgr =
752 do_GetService("@mozilla.org/gfx/screenmanager;1", &result);
753 if (NS_FAILED(result)) {
754 return result;
757 nsCOMPtr<nsIScreen> screen;
759 if (aRelative) {
760 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aRelative));
761 if (base) {
762 rect = RoundedToInt(base->GetPositionAndSize() /
763 base->DevicePixelsPerDesktopPixel());
764 // if centering on screen, convert that to the corresponding screen
765 if (aScreen) {
766 screen = screenmgr->ScreenForRect(rect);
767 } else {
768 windowCoordinates = true;
772 if (!aRelative) {
773 if (!mOpenerScreenRect.IsEmpty()) {
774 screen = screenmgr->ScreenForRect(mOpenerScreenRect);
775 } else {
776 screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
780 if (aScreen && screen) {
781 rect = screen->GetAvailRectDisplayPix();
782 screenCoordinates = true;
785 if (!screenCoordinates && !windowCoordinates) {
786 return NS_ERROR_FAILURE;
789 NS_ASSERTION(mWindow, "what, no window?");
790 const LayoutDeviceIntSize ourDevSize = GetSize();
791 const DesktopIntSize ourSize =
792 RoundedToInt(ourDevSize / DevicePixelsPerDesktopPixel());
793 auto newPos =
794 rect.TopLeft() +
795 DesktopIntPoint((rect.width - ourSize.width) / 2,
796 (rect.height - ourSize.height) / (aAlert ? 3 : 2));
797 if (windowCoordinates) {
798 mWindow->ConstrainPosition(newPos);
801 SetPositionDesktopPix(newPos.x, newPos.y);
803 // If moving the window caused it to change size, re-do the centering.
804 if (GetSize() != ourDevSize) {
805 return Center(aRelative, aScreen, aAlert);
807 return NS_OK;
810 NS_IMETHODIMP AppWindow::Repaint(bool aForce) {
811 // XXX First Check In
812 NS_ASSERTION(false, "Not Yet Implemented");
813 return NS_OK;
816 NS_IMETHODIMP AppWindow::GetParentWidget(nsIWidget** aParentWidget) {
817 NS_ENSURE_ARG_POINTER(aParentWidget);
818 NS_ENSURE_STATE(mWindow);
820 NS_IF_ADDREF(*aParentWidget = mWindow->GetParent());
821 return NS_OK;
824 NS_IMETHODIMP AppWindow::SetParentWidget(nsIWidget* aParentWidget) {
825 // XXX First Check In
826 NS_ASSERTION(false, "Not Yet Implemented");
827 return NS_OK;
830 NS_IMETHODIMP AppWindow::GetNativeHandle(nsAString& aNativeHandle) {
831 nsCOMPtr<nsIWidget> mainWidget;
832 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(mainWidget)),
833 NS_ERROR_FAILURE);
835 if (mainWidget) {
836 nativeWindow nativeWindowPtr = mainWidget->GetNativeData(NS_NATIVE_WINDOW);
837 /* the nativeWindow pointer is converted to and exposed as a string. This
838 is a more reliable way not to lose information (as opposed to JS
839 |Number| for instance) */
840 aNativeHandle =
841 NS_ConvertASCIItoUTF16(nsPrintfCString("0x%p", nativeWindowPtr));
844 return NS_OK;
847 NS_IMETHODIMP AppWindow::GetVisibility(bool* aVisibility) {
848 NS_ENSURE_ARG_POINTER(aVisibility);
850 // Always claim to be visible for now. See bug
851 // https://bugzilla.mozilla.org/show_bug.cgi?id=306245.
853 *aVisibility = true;
855 return NS_OK;
858 NS_IMETHODIMP AppWindow::SetVisibility(bool aVisibility) {
859 if (!mChromeLoaded) {
860 mShowAfterLoad = aVisibility;
861 return NS_OK;
864 if (mDebuting) {
865 return NS_OK;
868 NS_ENSURE_STATE(mDocShell);
870 mDebuting = true; // (Show / Focus is recursive)
872 // XXXTAB Do we really need to show docshell and the window? Isn't
873 // the window good enough?
874 mDocShell->SetVisibility(aVisibility);
875 // Store locally so it doesn't die on us. 'Show' can result in the window
876 // being closed with AppWindow::Destroy being called. That would set
877 // mWindow to null and possibly destroy the nsIWidget while its Show method
878 // is on the stack. We need to keep it alive until Show finishes.
879 nsCOMPtr<nsIWidget> window = mWindow;
880 window->Show(aVisibility);
882 // NOTE(emilio): A bit hacky, but we need to synchronously trigger resizes
883 // for remote frames here if we're a sized popup (mDominantClientSize=true).
885 // This is because what we do to show a popup window with a specified size is
886 // to wait until the chrome loads (and gets sized, and thus laid out at a
887 // particular pre-size), then size the window, and call Show(), which ends up
888 // here.
890 // After bug 1917458, that remote browser resize would happen asynchronously,
891 // which means content might be able to observe the old size unexpectedly.
892 if (aVisibility && mDominantClientSize) {
893 if (RefPtr doc = mDocShell->GetDocument()) {
894 doc->SynchronouslyUpdateRemoteBrowserDimensions();
898 nsCOMPtr<nsIWindowMediator> windowMediator(
899 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
900 if (windowMediator)
901 windowMediator->UpdateWindowTimeStamp(static_cast<nsIAppWindow*>(this));
903 // notify observers so that we can hide the splash screen if possible
904 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
905 NS_ASSERTION(obssvc, "Couldn't get observer service.");
906 if (obssvc) {
907 obssvc->NotifyObservers(static_cast<nsIAppWindow*>(this),
908 "xul-window-visible", nullptr);
911 mDebuting = false;
912 return NS_OK;
915 NS_IMETHODIMP AppWindow::GetEnabled(bool* aEnabled) {
916 NS_ENSURE_ARG_POINTER(aEnabled);
918 if (mWindow) {
919 *aEnabled = mWindow->IsEnabled();
920 return NS_OK;
923 *aEnabled = true; // better guess than most
924 return NS_ERROR_FAILURE;
927 NS_IMETHODIMP AppWindow::SetEnabled(bool aEnable) {
928 if (mWindow) {
929 mWindow->Enable(aEnable);
930 return NS_OK;
932 return NS_ERROR_FAILURE;
935 NS_IMETHODIMP AppWindow::GetMainWidget(nsIWidget** aMainWidget) {
936 NS_ENSURE_ARG_POINTER(aMainWidget);
937 NS_IF_ADDREF(*aMainWidget = mWindow);
938 return NS_OK;
941 NS_IMETHODIMP AppWindow::GetTitle(nsAString& aTitle) {
942 aTitle = mTitle;
943 return NS_OK;
946 NS_IMETHODIMP AppWindow::SetTitle(const nsAString& aTitle) {
947 NS_ENSURE_STATE(mWindow);
948 mTitle.Assign(aTitle);
949 mTitle.StripCRLF();
950 NS_ENSURE_SUCCESS(mWindow->SetTitle(mTitle), NS_ERROR_FAILURE);
951 return NS_OK;
954 //*****************************************************************************
955 // AppWindow: Helpers
956 //*****************************************************************************
958 NS_IMETHODIMP AppWindow::EnsureChromeTreeOwner() {
959 if (mChromeTreeOwner) return NS_OK;
961 mChromeTreeOwner = new nsChromeTreeOwner();
962 NS_ADDREF(mChromeTreeOwner);
963 mChromeTreeOwner->AppWindow(this);
965 return NS_OK;
968 NS_IMETHODIMP AppWindow::EnsureContentTreeOwner() {
969 if (mContentTreeOwner) return NS_OK;
971 mContentTreeOwner = new nsContentTreeOwner(false);
972 NS_ADDREF(mContentTreeOwner);
973 mContentTreeOwner->AppWindow(this);
975 return NS_OK;
978 NS_IMETHODIMP AppWindow::EnsurePrimaryContentTreeOwner() {
979 if (mPrimaryContentTreeOwner) return NS_OK;
981 mPrimaryContentTreeOwner = new nsContentTreeOwner(true);
982 NS_ADDREF(mPrimaryContentTreeOwner);
983 mPrimaryContentTreeOwner->AppWindow(this);
985 return NS_OK;
988 NS_IMETHODIMP AppWindow::EnsurePrompter() {
989 if (mPrompter) return NS_OK;
991 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
992 nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow));
993 if (NS_SUCCEEDED(rv)) {
994 nsCOMPtr<nsIWindowWatcher> wwatch =
995 do_GetService(NS_WINDOWWATCHER_CONTRACTID);
996 if (wwatch) wwatch->GetNewPrompter(ourWindow, getter_AddRefs(mPrompter));
998 return mPrompter ? NS_OK : NS_ERROR_FAILURE;
1001 NS_IMETHODIMP AppWindow::EnsureAuthPrompter() {
1002 if (mAuthPrompter) return NS_OK;
1004 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1005 nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow));
1006 if (NS_SUCCEEDED(rv)) {
1007 nsCOMPtr<nsIWindowWatcher> wwatch(
1008 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
1009 if (wwatch)
1010 wwatch->GetNewAuthPrompter(ourWindow, getter_AddRefs(mAuthPrompter));
1012 return mAuthPrompter ? NS_OK : NS_ERROR_FAILURE;
1015 NS_IMETHODIMP AppWindow::GetAvailScreenSize(int32_t* aAvailWidth,
1016 int32_t* aAvailHeight) {
1017 nsCOMPtr<mozIDOMWindowProxy> domWindow;
1018 GetWindowDOMWindow(getter_AddRefs(domWindow));
1019 NS_ENSURE_STATE(domWindow);
1021 auto* window = nsGlobalWindowOuter::Cast(domWindow);
1023 RefPtr<nsScreen> screen = window->GetScreen();
1024 NS_ENSURE_STATE(screen);
1026 *aAvailWidth = screen->AvailWidth();
1027 *aAvailHeight = screen->AvailHeight();
1028 return NS_OK;
1031 // Rounds window size to 1000x1000, or, if there isn't enough available
1032 // screen space, to a multiple of 200x100.
1033 NS_IMETHODIMP AppWindow::ForceRoundedDimensions() {
1034 if (mIsHiddenWindow) {
1035 return NS_OK;
1038 CSSToLayoutDeviceScale scale = UnscaledDevicePixelsPerCSSPixel();
1040 CSSIntSize availSizeCSS;
1041 GetAvailScreenSize(&availSizeCSS.width, &availSizeCSS.height);
1043 // To get correct chrome size, we have to resize the window to a proper
1044 // size first. So, here, we size it to its available size.
1045 SetSpecifiedSize(availSizeCSS.width, availSizeCSS.height);
1047 // Get the current window size for calculating chrome UI size.
1048 CSSIntSize windowSizeCSS = RoundedToInt(GetSize() / scale);
1050 // Get the content size for calculating chrome UI size.
1051 LayoutDeviceIntSize contentSizeDev;
1052 GetPrimaryContentSize(&contentSizeDev.width, &contentSizeDev.height);
1053 CSSIntSize contentSizeCSS = RoundedToInt(contentSizeDev / scale);
1055 // Calculate the chrome UI size.
1056 CSSIntSize chromeSizeCSS = windowSizeCSS - contentSizeCSS;
1058 CSSIntSize targetSizeCSS;
1059 // Here, we use the available screen dimensions as the input dimensions to
1060 // force the window to be rounded as the maximum available content size.
1061 nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
1062 chromeSizeCSS.width, chromeSizeCSS.height, availSizeCSS.width,
1063 availSizeCSS.height, availSizeCSS.width, availSizeCSS.height,
1064 false, // aSetOuterWidth
1065 false, // aSetOuterHeight
1066 &targetSizeCSS.width, &targetSizeCSS.height);
1068 LayoutDeviceIntSize targetSizeDev = RoundedToInt(targetSizeCSS * scale);
1070 SetPrimaryContentSize(targetSizeDev.width, targetSizeDev.height);
1072 return NS_OK;
1075 void AppWindow::OnChromeLoaded() {
1076 nsresult rv = EnsureContentTreeOwner();
1078 if (NS_SUCCEEDED(rv)) {
1079 mChromeLoaded = true;
1080 ApplyChromeFlags();
1081 SyncAttributesToWidget();
1082 if (mWindow) {
1083 SizeShell();
1084 if (mShowAfterLoad) {
1085 SetVisibility(true);
1087 AddTooltipSupport();
1089 // At this point the window may have been closed already during Show() or
1090 // SyncAttributesToWidget(), so AppWindow::Destroy may already have been
1091 // called. Take care!
1093 mPersistentAttributesMask += AllPersistentAttributes();
1096 bool AppWindow::NeedsTooltipListener() {
1097 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1098 if (!docShellElement || docShellElement->IsXULElement()) {
1099 // Tooltips in XUL are handled by each element.
1100 return false;
1102 // All other non-XUL document types need a tooltip listener.
1103 return true;
1106 void AppWindow::AddTooltipSupport() {
1107 if (!NeedsTooltipListener()) {
1108 return;
1110 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
1111 if (!listener) {
1112 return;
1115 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1116 MOZ_ASSERT(docShellElement);
1117 listener->AddTooltipSupport(docShellElement);
1120 void AppWindow::RemoveTooltipSupport() {
1121 if (!NeedsTooltipListener()) {
1122 return;
1124 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
1125 if (!listener) {
1126 return;
1129 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1130 MOZ_ASSERT(docShellElement);
1131 listener->RemoveTooltipSupport(docShellElement);
1134 static Maybe<int32_t> ReadIntAttribute(const Element& aElement,
1135 nsAtom* aPrimary,
1136 nsAtom* aSecondary = nullptr) {
1137 nsAutoString attrString;
1138 if (!aElement.GetAttr(aPrimary, attrString)) {
1139 if (aSecondary) {
1140 return ReadIntAttribute(aElement, aSecondary);
1142 return Nothing();
1145 nsresult res = NS_OK;
1146 int32_t ret = attrString.ToInteger(&res);
1147 return NS_SUCCEEDED(res) ? Some(ret) : Nothing();
1150 // If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes
1151 // to fit to the screen when staggering windows; if they're negative,
1152 // we use the window's current size instead.
1153 bool AppWindow::LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight) {
1154 // if we're the hidden window, don't try to validate our size/position. We're
1155 // special.
1156 if (mIsHiddenWindow) {
1157 return false;
1160 // If we're not in the normal sizemode, don't move the window around.
1161 if (mWindow->SizeMode() != nsSizeMode_Normal) {
1162 return false;
1165 RefPtr<dom::Element> root = GetWindowDOMElement();
1166 NS_ENSURE_TRUE(root, false);
1168 const LayoutDeviceIntRect devRect = GetPositionAndSize();
1170 // Convert to global display pixels for consistent window management across
1171 // screens with diverse resolutions
1172 const DesktopIntPoint curPoint =
1173 RoundedToInt(devRect.TopLeft() / DevicePixelsPerDesktopPixel());
1175 // For size, use specified value if > 0, else current value
1176 CSSIntSize cssSize(aSpecWidth, aSpecHeight);
1178 CSSIntSize currentSize =
1179 RoundedToInt(devRect.Size() / UnscaledDevicePixelsPerCSSPixel());
1180 if (aSpecHeight <= 0) {
1181 cssSize.height = currentSize.height;
1183 if (aSpecWidth <= 0) {
1184 cssSize.width = currentSize.width;
1188 // Obtain the position information from the <xul:window> element.
1189 DesktopIntPoint specPoint = curPoint;
1190 bool gotPosition = false;
1192 // Also read lowercase screenx/y because the front-end sometimes sets these
1193 // via setAttribute on HTML documents like about:blank, and stuff gets
1194 // lowercased.
1196 // TODO(emilio): We should probably rename screenX/Y to screen-x/y to
1197 // prevent this impedance mismatch.
1198 if (auto attr =
1199 ReadIntAttribute(*root, nsGkAtoms::screenX, nsGkAtoms::screenx)) {
1200 specPoint.x = *attr;
1201 gotPosition = true;
1204 if (auto attr =
1205 ReadIntAttribute(*root, nsGkAtoms::screenY, nsGkAtoms::screeny)) {
1206 specPoint.y = *attr;
1207 gotPosition = true;
1210 if (gotPosition) {
1211 // Our position will be relative to our parent, if any
1212 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
1213 if (parent) {
1214 const DesktopIntPoint parentPos = RoundedToInt(
1215 parent->GetPosition() / parent->DevicePixelsPerDesktopPixel());
1216 specPoint += parentPos;
1217 } else {
1218 StaggerPosition(specPoint.x.value, specPoint.y.value, cssSize.width,
1219 cssSize.height);
1222 mWindow->ConstrainPosition(specPoint);
1223 if (specPoint != curPoint) {
1224 SetPositionDesktopPix(specPoint.x, specPoint.y);
1226 return gotPosition;
1229 static Maybe<int32_t> ReadSize(const Element& aElement, nsAtom* aAttr,
1230 nsAtom* aMinAttr, nsAtom* aMaxAttr) {
1231 Maybe<int32_t> attr = ReadIntAttribute(aElement, aAttr);
1232 if (!attr) {
1233 return Nothing();
1236 int32_t min =
1237 std::max(100, ReadIntAttribute(aElement, aMinAttr).valueOr(100));
1238 int32_t max = ReadIntAttribute(aElement, aMaxAttr)
1239 .valueOr(std::numeric_limits<int32_t>::max());
1241 return Some(std::clamp(*attr, min, max));
1244 bool AppWindow::LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight) {
1245 bool gotSize = false;
1247 // if we're the hidden window, don't try to validate our size/position. We're
1248 // special.
1249 if (mIsHiddenWindow) {
1250 return false;
1253 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1254 NS_ENSURE_TRUE(windowElement, false);
1256 // Obtain the sizing information from the <xul:window> element.
1257 aSpecWidth = 100;
1258 aSpecHeight = 100;
1260 if (auto width = ReadSize(*windowElement, nsGkAtoms::width,
1261 nsGkAtoms::minwidth, nsGkAtoms::maxwidth)) {
1262 aSpecWidth = *width;
1263 gotSize = true;
1266 if (auto height = ReadSize(*windowElement, nsGkAtoms::height,
1267 nsGkAtoms::minheight, nsGkAtoms::maxheight)) {
1268 aSpecHeight = *height;
1269 gotSize = true;
1272 return gotSize;
1275 void AppWindow::SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight) {
1276 // These are in CSS pixels of the main window.
1277 // TODO(emilio): In my testing we usually have a pres context around, can we
1278 // just use it? That'd simplify the coordinate calculations.
1280 int32_t screenWidth;
1281 int32_t screenHeight;
1283 if (NS_SUCCEEDED(GetAvailScreenSize(&screenWidth, &screenHeight))) {
1284 if (aSpecWidth > screenWidth) {
1285 aSpecWidth = screenWidth;
1287 if (aSpecHeight > screenHeight) {
1288 aSpecHeight = screenHeight;
1293 NS_ASSERTION(mWindow, "we expected to have a window already");
1295 mIntrinsicallySized = false;
1297 // Convert specified values to device pixels, and resize
1298 auto newSize = RoundedToInt(CSSIntSize(aSpecWidth, aSpecHeight) *
1299 UnscaledDevicePixelsPerCSSPixel());
1301 // Note: Because of the asynchronous resizing on Linux we have to call
1302 // SetSize even when the size doesn't appear to change. A previous call that
1303 // has yet to complete can still change the size. We want the latest call to
1304 // define the final size.
1305 SetSize(newSize.width, newSize.height, false);
1308 /* Miscellaneous persistent attributes are attributes named in the
1309 |persist| attribute, other than size and position. Those are special
1310 because it's important to load those before one of the misc
1311 attributes (sizemode) and they require extra processing. */
1312 bool AppWindow::UpdateWindowStateFromMiscXULAttributes() {
1313 /* There are no misc attributes of interest to the hidden window.
1314 It's especially important not to try to validate that window's
1315 size or position, because some platforms (Mac OS X) need to
1316 make it visible and offscreen. */
1317 if (mIsHiddenWindow) {
1318 return false;
1321 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1322 NS_ENSURE_TRUE(windowElement, false);
1324 nsAutoString stateString;
1325 nsSizeMode sizeMode = nsSizeMode_Normal;
1327 // If we are told to ignore the size mode attribute, force
1328 // normal sizemode.
1329 if (mIgnoreXULSizeMode) {
1330 windowElement->SetAttr(nsGkAtoms::sizemode, SIZEMODE_NORMAL,
1331 IgnoreErrors());
1332 } else {
1333 // Otherwise, read sizemode from DOM and, if the window is resizable,
1334 // set it later.
1335 windowElement->GetAttr(nsGkAtoms::sizemode, stateString);
1336 if ((stateString.Equals(SIZEMODE_MAXIMIZED) ||
1337 stateString.Equals(SIZEMODE_FULLSCREEN))) {
1338 /* Honor request to maximize only if the window is sizable.
1339 An unsizable, unmaximizable, yet maximized window confuses
1340 Windows OS and is something of a travesty, anyway. */
1341 if (mChromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
1342 mIntrinsicallySized = false;
1344 sizeMode = stateString.Equals(SIZEMODE_MAXIMIZED)
1345 ? nsSizeMode_Maximized
1346 : nsSizeMode_Fullscreen;
1351 if (sizeMode == nsSizeMode_Fullscreen) {
1352 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1353 GetWindowDOMWindow(getter_AddRefs(ourWindow));
1354 auto* piWindow = nsPIDOMWindowOuter::From(ourWindow);
1355 piWindow->SetFullScreen(true);
1356 } else {
1357 // For maximized windows, ignore the XUL size and position attributes,
1358 // as setting them would set the window back to normal sizemode.
1359 if (sizeMode == nsSizeMode_Maximized) {
1360 mIgnoreXULSize = true;
1361 mIgnoreXULPosition = true;
1363 mWindow->SetSizeMode(sizeMode);
1365 return true;
1368 /* Stagger windows of the same type so they don't appear on top of each other.
1369 This code does have a scary double loop -- it'll keep passing through
1370 the entire list of open windows until it finds a non-collision. Doesn't
1371 seem to be a problem, but it deserves watching.
1372 The aRequested{X,Y} parameters here are in desktop pixels;
1373 the aSpec{Width,Height} parameters are CSS pixel dimensions.
1375 void AppWindow::StaggerPosition(int32_t& aRequestedX, int32_t& aRequestedY,
1376 int32_t aSpecWidth, int32_t aSpecHeight) {
1377 // These "constants" will be converted from CSS to desktop pixels
1378 // for the appropriate screen, assuming we find a screen to use...
1379 // hence they're not actually declared const here.
1380 int32_t kOffset = 22;
1381 uint32_t kSlop = 4;
1383 bool keepTrying;
1384 int bouncedX = 0, // bounced off vertical edge of screen
1385 bouncedY = 0; // bounced off horizontal edge
1387 // look for any other windows of this type
1388 nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
1389 if (!wm) return;
1391 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1392 if (!windowElement) return;
1394 nsCOMPtr<nsIAppWindow> ourAppWindow(this);
1396 nsAutoString windowType;
1397 windowElement->GetAttr(nsGkAtoms::windowtype, windowType);
1399 DesktopIntRect screenRect;
1400 bool gotScreen = false;
1402 { // fetch screen coordinates
1403 nsCOMPtr<nsIScreenManager> screenMgr(
1404 do_GetService("@mozilla.org/gfx/screenmanager;1"));
1405 if (screenMgr) {
1406 nsCOMPtr<nsIScreen> ourScreen;
1407 // The coordinates here are already display pixels
1408 // XXX aSpecWidth and aSpecHeight are CSS pixels!
1409 screenMgr->ScreenForRect(aRequestedX, aRequestedY, aSpecWidth,
1410 aSpecHeight, getter_AddRefs(ourScreen));
1411 if (ourScreen) {
1412 screenRect = ourScreen->GetAvailRectDisplayPix();
1414 // Get the screen's scaling factors and convert staggering constants
1415 // from CSS px to desktop pixel units
1416 auto scale = ourScreen->GetCSSToDesktopScale();
1417 kOffset = (CSSCoord(kOffset) * scale).Rounded();
1418 kSlop = (CSSCoord(kSlop) * scale).Rounded();
1419 // Convert dimensions from CSS to desktop pixels
1420 aSpecWidth = (CSSCoord(aSpecWidth) * scale).Rounded();
1421 aSpecHeight = (CSSCoord(aSpecHeight) * scale).Rounded();
1422 gotScreen = true;
1427 // One full pass through all windows of this type, repeat until no collisions.
1428 do {
1429 keepTrying = false;
1430 nsCOMPtr<nsISimpleEnumerator> windowList;
1431 wm->GetAppWindowEnumerator(windowType.get(), getter_AddRefs(windowList));
1433 if (!windowList) break;
1435 // One full pass through all windows of this type, offset and stop on
1436 // collision.
1437 do {
1438 bool more;
1439 windowList->HasMoreElements(&more);
1440 if (!more) break;
1442 nsCOMPtr<nsISupports> supportsWindow;
1443 windowList->GetNext(getter_AddRefs(supportsWindow));
1445 nsCOMPtr<nsIAppWindow> listAppWindow(do_QueryInterface(supportsWindow));
1446 if (listAppWindow != ourAppWindow) {
1447 int32_t listX, listY;
1448 nsCOMPtr<nsIBaseWindow> listBaseWindow(
1449 do_QueryInterface(supportsWindow));
1450 listBaseWindow->GetPosition(&listX, &listY);
1451 double scale;
1452 if (NS_SUCCEEDED(
1453 listBaseWindow->GetDevicePixelsPerDesktopPixel(&scale))) {
1454 listX = NSToIntRound(listX / scale);
1455 listY = NSToIntRound(listY / scale);
1458 if (Abs(listX - aRequestedX) <= kSlop &&
1459 Abs(listY - aRequestedY) <= kSlop) {
1460 // collision! offset and start over
1461 if (bouncedX & 0x1)
1462 aRequestedX -= kOffset;
1463 else
1464 aRequestedX += kOffset;
1465 aRequestedY += kOffset;
1467 if (gotScreen) {
1468 // if we're moving to the right and we need to bounce...
1469 if (!(bouncedX & 0x1) &&
1470 ((aRequestedX + aSpecWidth) > screenRect.XMost())) {
1471 aRequestedX = screenRect.XMost() - aSpecWidth;
1472 ++bouncedX;
1475 // if we're moving to the left and we need to bounce...
1476 if ((bouncedX & 0x1) && aRequestedX < screenRect.X()) {
1477 aRequestedX = screenRect.X();
1478 ++bouncedX;
1481 // if we hit the bottom then bounce to the top
1482 if (aRequestedY + aSpecHeight > screenRect.YMost()) {
1483 aRequestedY = screenRect.Y();
1484 ++bouncedY;
1488 /* loop around again,
1489 but it's time to give up once we've covered the screen.
1490 there's a potential infinite loop with lots of windows. */
1491 keepTrying = bouncedX < 2 || bouncedY == 0;
1492 break;
1495 } while (true);
1496 } while (keepTrying);
1499 void AppWindow::SyncAttributesToWidget() {
1500 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1501 if (!windowElement) return;
1503 MOZ_DIAGNOSTIC_ASSERT(mWindow, "No widget on SyncAttributesToWidget?");
1505 nsAutoString attr;
1507 // Some attributes can change the client size (e.g. customtitlebar on Windows
1508 // and MacOS). But we might want to keep it.
1509 const LayoutDeviceIntSize oldClientSize = mWindow->GetClientSize();
1510 // We have to check now whether we want to restore the client size, as any
1511 // change in size will reset its state.
1512 bool maintainClientSize = mDominantClientSize;
1514 // "hidechrome" attribute
1515 // FIXME(emilio): This should arguably be
1516 // HideWindowChrome(windowElement->GetBoolAttr(...)), but that has
1517 // side-effects in some platforms.
1518 if (windowElement->GetBoolAttr(nsGkAtoms::hidechrome)) {
1519 mWindow->HideWindowChrome(true);
1521 NS_ENSURE_TRUE_VOID(mWindow);
1523 // "customtitlebar" attribute
1524 // FIXME(emilio): This should arguably be
1525 // SetCustomTitlebar(windowElement->GetBoolAttr(...)), but that breaks with
1526 // the early blank window which sets the custom titlebar via
1527 // nsIDOMWindowUtils...
1528 if (windowElement->GetBoolAttr(nsGkAtoms::customtitlebar)) {
1529 mWindow->SetCustomTitlebar(true);
1532 NS_ENSURE_TRUE_VOID(mWindow);
1534 mWindow->SetMicaBackdrop(windowElement->GetBoolAttr(nsGkAtoms::windowsmica));
1535 NS_ENSURE_TRUE_VOID(mWindow);
1537 // "windowtype", "windowclass", "windowname" attributes
1538 nsAutoString windowClassAttr, windowNameAttr;
1539 windowElement->GetAttr(nsGkAtoms::windowtype, attr);
1540 windowElement->GetAttribute(u"windowclass"_ns, windowClassAttr);
1541 windowElement->GetAttribute(u"windowname"_ns, windowNameAttr);
1542 mWindow->SetWindowClass(attr, windowClassAttr, windowNameAttr);
1544 NS_ENSURE_TRUE_VOID(mWindow);
1546 // Only change blank window status once we're loaded, so that a
1547 // partially-loaded browser window doesn't start painting early.
1548 if (mChromeLoaded) {
1549 mWindow->SetIsEarlyBlankWindow(attr.EqualsLiteral("navigator:blank"));
1550 NS_ENSURE_TRUE_VOID(mWindow);
1553 // "icon" attribute
1554 windowElement->GetAttribute(u"icon"_ns, attr);
1555 if (!attr.IsEmpty()) {
1556 mWindow->SetIcon(attr);
1557 NS_ENSURE_TRUE_VOID(mWindow);
1560 // "drawtitle" attribute
1561 mWindow->SetDrawsTitle(windowElement->GetBoolAttr(nsGkAtoms::drawtitle));
1562 NS_ENSURE_TRUE_VOID(mWindow);
1564 // "toggletoolbar" attribute
1565 mWindow->SetShowsToolbarButton(
1566 windowElement->HasAttribute(u"toggletoolbar"_ns));
1567 NS_ENSURE_TRUE_VOID(mWindow);
1569 // "macnativefullscreen" attribute
1570 mWindow->SetSupportsNativeFullscreen(
1571 windowElement->HasAttribute(u"macnativefullscreen"_ns));
1572 NS_ENSURE_TRUE_VOID(mWindow);
1574 // "macanimationtype" attribute
1575 windowElement->GetAttribute(u"macanimationtype"_ns, attr);
1576 if (attr.EqualsLiteral("document")) {
1577 mWindow->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation);
1580 // Check if the client size did change and if we want to restore it.
1581 if (maintainClientSize && mWindow->SizeMode() == nsSizeMode_Normal &&
1582 oldClientSize != mWindow->GetClientSize()) {
1583 mWindow->ResizeClient(oldClientSize / mWindow->GetDesktopToDeviceScale(),
1584 true);
1585 mDominantClientSize = true;
1589 enum class ConversionDirection {
1590 InnerToOuter,
1591 OuterToInner,
1594 static void ConvertWindowSize(nsIAppWindow* aWin, const nsAtom* aAttr,
1595 ConversionDirection aDirection,
1596 nsAString& aInOutString) {
1597 MOZ_ASSERT(aWin);
1598 MOZ_ASSERT(aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height);
1600 nsresult rv;
1601 int32_t size = aInOutString.ToInteger(&rv);
1602 if (NS_FAILED(rv)) {
1603 return;
1606 int32_t sizeDiff = aAttr == nsGkAtoms::width
1607 ? aWin->GetOuterToInnerWidthDifferenceInCSSPixels()
1608 : aWin->GetOuterToInnerHeightDifferenceInCSSPixels();
1610 if (!sizeDiff) {
1611 return;
1614 int32_t multiplier = aDirection == ConversionDirection::InnerToOuter ? 1 : -1;
1616 CopyASCIItoUTF16(nsPrintfCString("%d", size + multiplier * sizeDiff),
1617 aInOutString);
1620 nsresult AppWindow::GetPersistentValue(const nsAtom* aAttr, nsAString& aValue) {
1621 if (!XRE_IsParentProcess()) {
1622 // The XULStore is only available in the parent process.
1623 return NS_ERROR_UNEXPECTED;
1626 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1627 if (!docShellElement) {
1628 return NS_ERROR_FAILURE;
1631 nsAutoString windowElementId;
1632 docShellElement->GetId(windowElementId);
1633 // Elements must have an ID to be persisted.
1634 if (windowElementId.IsEmpty()) {
1635 return NS_OK;
1638 RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
1639 nsIURI* docURI = ownerDoc->GetDocumentURI();
1640 if (!docURI) {
1641 return NS_ERROR_FAILURE;
1643 nsAutoCString utf8uri;
1644 nsresult rv = docURI->GetSpec(utf8uri);
1645 NS_ENSURE_SUCCESS(rv, rv);
1646 NS_ConvertUTF8toUTF16 uri(utf8uri);
1648 if (!mLocalStore) {
1649 mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1650 if (NS_WARN_IF(!mLocalStore)) {
1651 return NS_ERROR_NOT_INITIALIZED;
1655 rv = mLocalStore->GetValue(uri, windowElementId, nsDependentAtomString(aAttr),
1656 aValue);
1657 if (NS_WARN_IF(NS_FAILED(rv))) {
1658 return rv;
1661 if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
1662 // Convert attributes from outer size to inner size for top-level
1663 // windows, see bug 1444525 & co.
1664 ConvertWindowSize(this, aAttr, ConversionDirection::OuterToInner, aValue);
1667 return NS_OK;
1670 nsresult AppWindow::GetDocXulStoreKeys(nsString& aUriSpec,
1671 nsString& aWindowElementId) {
1672 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1673 if (!docShellElement) {
1674 return NS_ERROR_FAILURE;
1677 docShellElement->GetId(aWindowElementId);
1678 // Match the behavior of XULPersist and only persist values if the element
1679 // has an ID.
1680 if (aWindowElementId.IsEmpty()) {
1681 return NS_OK;
1684 RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
1685 nsIURI* docURI = ownerDoc->GetDocumentURI();
1686 if (!docURI) {
1687 return NS_ERROR_FAILURE;
1690 nsAutoCString utf8uri;
1691 nsresult rv = docURI->GetSpec(utf8uri);
1692 if (NS_WARN_IF(NS_FAILED(rv))) {
1693 return rv;
1696 aUriSpec = NS_ConvertUTF8toUTF16(utf8uri);
1698 return NS_OK;
1701 nsresult AppWindow::MaybeSaveEarlyWindowPersistentValues(
1702 const LayoutDeviceIntRect& aRect) {
1703 #ifdef XP_WIN
1704 nsAutoString uri;
1705 nsAutoString windowElementId;
1706 nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
1708 if (NS_WARN_IF(NS_FAILED(rv))) {
1709 return rv;
1712 if (!windowElementId.EqualsLiteral("main-window") ||
1713 !uri.EqualsLiteral("chrome://browser/content/browser.xhtml")) {
1714 return NS_OK;
1717 SkeletonUISettings settings;
1719 settings.screenX = aRect.X();
1720 settings.screenY = aRect.Y();
1721 settings.width = aRect.Width();
1722 settings.height = aRect.Height();
1724 settings.maximized = mWindow->SizeMode() == nsSizeMode_Maximized;
1725 settings.cssToDevPixelScaling = UnscaledDevicePixelsPerCSSPixel().scale;
1727 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1728 Document* doc = windowElement->GetComposedDoc();
1729 Element* urlbarEl = doc->GetElementById(u"urlbar"_ns);
1731 nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow();
1732 nsCOMPtr<nsIDOMWindowUtils> utils =
1733 nsGlobalWindowOuter::Cast(window)->WindowUtils();
1734 RefPtr<dom::DOMRect> urlbarRect;
1735 rv = utils->GetBoundsWithoutFlushing(urlbarEl, getter_AddRefs(urlbarRect));
1736 if (NS_WARN_IF(NS_FAILED(rv))) {
1737 return rv;
1740 double urlbarX = urlbarRect->X();
1741 double urlbarWidth = urlbarRect->Width();
1743 // Hard-coding the following values and this behavior in general is rather
1744 // fragile, and can easily get out of sync with the actual front-end values.
1745 // This is not intended as a long-term solution, but only as the relatively
1746 // straightforward implementation of an experimental feature. If we want to
1747 // ship the skeleton UI to all users, we should strongly consider a more
1748 // robust solution than this. The vertical position of the urlbar will be
1749 // fixed.
1750 nsAutoString attributeValue;
1751 urlbarEl->GetAttribute(u"breakout-extend"_ns, attributeValue);
1752 // Scale down the urlbar if it is focused
1753 if (attributeValue.EqualsLiteral("true")) {
1754 // defined in browser.inc.css as 2px
1755 int urlbarBreakoutExtend = 2;
1756 // defined in urlbar-searchbar.inc.css as 5px
1757 int urlbarMarginInline = 5;
1759 // breakout-extend measurements are defined in urlbar-searchbar.inc.css
1760 urlbarX += (double)(urlbarBreakoutExtend + urlbarMarginInline);
1761 urlbarWidth -= (double)(2 * (urlbarBreakoutExtend + urlbarMarginInline));
1763 CSSPixelSpan urlbar;
1764 urlbar.start = urlbarX;
1765 urlbar.end = urlbar.start + urlbarWidth;
1766 settings.urlbarSpan = urlbar;
1768 Element* navbar = doc->GetElementById(u"nav-bar"_ns);
1770 Element* searchbarEl = doc->GetElementById(u"searchbar"_ns);
1771 CSSPixelSpan searchbar;
1772 if (navbar->Contains(searchbarEl)) {
1773 RefPtr<dom::DOMRect> searchbarRect;
1774 rv = utils->GetBoundsWithoutFlushing(searchbarEl,
1775 getter_AddRefs(searchbarRect));
1776 if (NS_WARN_IF(NS_FAILED(rv))) {
1777 return rv;
1779 searchbar.start = searchbarRect->X();
1780 searchbar.end = searchbar.start + searchbarRect->Width();
1781 } else {
1782 // There is no searchbar in the UI
1783 searchbar.start = 0;
1784 searchbar.end = 0;
1786 settings.searchbarSpan = searchbar;
1788 nsAutoString bookmarksVisibility;
1789 Preferences::GetString("browser.toolbars.bookmarks.visibility",
1790 bookmarksVisibility);
1791 settings.bookmarksToolbarShown =
1792 bookmarksVisibility.EqualsLiteral("always") ||
1793 bookmarksVisibility.EqualsLiteral("newtab");
1795 Element* menubar = doc->GetElementById(u"toolbar-menubar"_ns);
1796 menubar->GetAttribute(u"autohide"_ns, attributeValue);
1797 settings.menubarShown = attributeValue.EqualsLiteral("false");
1799 ErrorResult err;
1800 nsCOMPtr<nsIHTMLCollection> toolbarSprings = navbar->GetElementsByTagNameNS(
1801 u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns,
1802 u"toolbarspring"_ns, err);
1803 if (err.Failed()) {
1804 return NS_ERROR_FAILURE;
1806 mozilla::Vector<CSSPixelSpan> springs;
1807 for (size_t i = 0; i < toolbarSprings->Length(); i++) {
1808 RefPtr<Element> springEl = toolbarSprings->Item(i);
1809 RefPtr<dom::DOMRect> springRect;
1810 rv = utils->GetBoundsWithoutFlushing(springEl, getter_AddRefs(springRect));
1811 if (NS_WARN_IF(NS_FAILED(rv))) {
1812 return rv;
1814 CSSPixelSpan spring;
1815 spring.start = springRect->X();
1816 spring.end = spring.start + springRect->Width();
1817 if (!settings.springs.append(spring)) {
1818 return NS_ERROR_FAILURE;
1822 settings.rtlEnabled = intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1824 bool isInTabletMode = false;
1825 bool const autoTouchModePref =
1826 Preferences::GetBool("browser.touchmode.auto", false);
1827 if (autoTouchModePref) {
1828 nsCOMPtr<nsIWindowsUIUtils> uiUtils(
1829 do_GetService("@mozilla.org/windows-ui-utils;1"));
1830 if (!NS_WARN_IF(!uiUtils)) {
1831 // We switch to the touch-optimized layout in both Win10 and Win11 tablet-
1832 // modes, since only the input mechanism is relevant. (See bug 1819421.)
1833 if (IsWin11OrLater()) {
1834 uiUtils->GetInWin11TabletMode(&isInTabletMode);
1835 } else {
1836 uiUtils->GetInWin10TabletMode(&isInTabletMode);
1841 if (isInTabletMode) {
1842 settings.uiDensity = SkeletonUIDensity::Touch;
1843 } else {
1844 int uiDensityPref = Preferences::GetInt("browser.uidensity", 0);
1845 switch (uiDensityPref) {
1846 case 0: {
1847 settings.uiDensity = SkeletonUIDensity::Default;
1848 break;
1850 case 1: {
1851 settings.uiDensity = SkeletonUIDensity::Compact;
1852 break;
1854 case 2: {
1855 settings.uiDensity = SkeletonUIDensity::Touch;
1856 break;
1861 settings.verticalTabs = Preferences::GetBool("sidebar.verticalTabs", false);
1863 Unused << PersistPreXULSkeletonUIValues(settings);
1864 #endif
1866 return NS_OK;
1869 nsresult AppWindow::SetPersistentValue(const nsAtom* aAttr,
1870 const nsAString& aValue) {
1871 if (!XRE_IsParentProcess()) {
1872 // The XULStore is only available in the parent process.
1873 return NS_ERROR_UNEXPECTED;
1876 nsAutoString uri;
1877 nsAutoString windowElementId;
1878 nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
1880 if (NS_FAILED(rv) || windowElementId.IsEmpty()) {
1881 return rv;
1884 nsAutoString maybeConvertedValue(aValue);
1885 if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
1886 // Make sure we store the <window> attributes as outer window size, see
1887 // bug 1444525 & co.
1888 ConvertWindowSize(this, aAttr, ConversionDirection::InnerToOuter,
1889 maybeConvertedValue);
1892 if (!mLocalStore) {
1893 mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1894 if (NS_WARN_IF(!mLocalStore)) {
1895 return NS_ERROR_NOT_INITIALIZED;
1899 return mLocalStore->SetValue(
1900 uri, windowElementId, nsDependentAtomString(aAttr), maybeConvertedValue);
1903 void AppWindow::MaybeSavePersistentPositionAndSize(
1904 PersistentAttributes aAttributes, Element& aRootElement,
1905 const nsAString& aPersistString, bool aShouldPersist) {
1906 if ((aAttributes & PersistentAttributes{PersistentAttribute::Position,
1907 PersistentAttribute::Size})
1908 .isEmpty()) {
1909 return;
1912 // get our size, position and mode to persist
1913 LayoutDeviceIntRect rect;
1914 if (NS_FAILED(mWindow->GetRestoredBounds(rect))) {
1915 return;
1918 // we use CSS pixels for size, but desktop pixels for position
1919 CSSToLayoutDeviceScale sizeScale = UnscaledDevicePixelsPerCSSPixel();
1920 DesktopToLayoutDeviceScale posScale = DevicePixelsPerDesktopPixel();
1922 // make our position relative to our parent, if any
1923 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
1924 if (parent) {
1925 int32_t parentX, parentY;
1926 if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
1927 rect.MoveBy(-parentX, -parentY);
1931 nsAutoString sizeString;
1932 // (only for size elements which are persisted)
1933 if (aAttributes.contains(PersistentAttribute::Position)) {
1934 if (aPersistString.Find(u"screenX") >= 0) {
1935 sizeString.Truncate();
1936 sizeString.AppendInt(NSToIntRound(rect.X() / posScale.scale));
1937 aRootElement.SetAttr(nsGkAtoms::screenX, sizeString, IgnoreErrors());
1938 if (aShouldPersist) {
1939 Unused << SetPersistentValue(nsGkAtoms::screenX, sizeString);
1942 if (aPersistString.Find(u"screenY") >= 0) {
1943 sizeString.Truncate();
1944 sizeString.AppendInt(NSToIntRound(rect.Y() / posScale.scale));
1945 aRootElement.SetAttr(nsGkAtoms::screenY, sizeString, IgnoreErrors());
1946 if (aShouldPersist) {
1947 Unused << SetPersistentValue(nsGkAtoms::screenY, sizeString);
1952 if (aAttributes.contains(PersistentAttribute::Size)) {
1953 LayoutDeviceIntRect innerRect =
1954 rect - GetOuterToInnerSizeDifference(mWindow);
1955 if (aPersistString.Find(u"width") >= 0) {
1956 sizeString.Truncate();
1957 sizeString.AppendInt(NSToIntRound(innerRect.Width() / sizeScale.scale));
1958 aRootElement.SetAttr(nsGkAtoms::width, sizeString, IgnoreErrors());
1959 if (aShouldPersist) {
1960 Unused << SetPersistentValue(nsGkAtoms::width, sizeString);
1963 if (aPersistString.Find(u"height") >= 0) {
1964 sizeString.Truncate();
1965 sizeString.AppendInt(NSToIntRound(innerRect.Height() / sizeScale.scale));
1966 aRootElement.SetAttr(nsGkAtoms::height, sizeString, IgnoreErrors());
1967 if (aShouldPersist) {
1968 Unused << SetPersistentValue(nsGkAtoms::height, sizeString);
1973 Unused << MaybeSaveEarlyWindowPersistentValues(rect);
1976 void AppWindow::MaybeSavePersistentMiscAttributes(
1977 PersistentAttributes aAttributes, Element& aRootElement,
1978 const nsAString& aPersistString, bool aShouldPersist) {
1979 if (!aAttributes.contains(PersistentAttribute::Misc)) {
1980 return;
1983 nsSizeMode sizeMode = mWindow->SizeMode();
1984 nsAutoString sizeString;
1985 if (sizeMode != nsSizeMode_Minimized) {
1986 if (sizeMode == nsSizeMode_Maximized) {
1987 sizeString.Assign(SIZEMODE_MAXIMIZED);
1988 } else if (sizeMode == nsSizeMode_Fullscreen) {
1989 sizeString.Assign(SIZEMODE_FULLSCREEN);
1990 } else {
1991 sizeString.Assign(SIZEMODE_NORMAL);
1993 aRootElement.SetAttr(nsGkAtoms::sizemode, sizeString, IgnoreErrors());
1994 if (aShouldPersist && aPersistString.Find(u"sizemode") >= 0) {
1995 Unused << SetPersistentValue(nsGkAtoms::sizemode, sizeString);
1998 aRootElement.SetAttribute(u"gtktiledwindow"_ns,
1999 mWindow->IsTiled() ? u"true"_ns : u"false"_ns,
2000 IgnoreErrors());
2003 void AppWindow::SavePersistentAttributes(
2004 const PersistentAttributes aAttributes) {
2005 // can happen when the persistence timer fires at an inopportune time
2006 // during window shutdown
2007 if (!mDocShell) {
2008 return;
2011 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
2012 if (!docShellElement) {
2013 return;
2016 nsAutoString persistString;
2017 docShellElement->GetAttr(nsGkAtoms::persist, persistString);
2018 if (persistString.IsEmpty()) { // quick check which sometimes helps
2019 mPersistentAttributesDirty.clear();
2020 return;
2023 bool shouldPersist = mWindow->SizeMode() != nsSizeMode_Fullscreen;
2024 MaybeSavePersistentPositionAndSize(aAttributes, *docShellElement,
2025 persistString, shouldPersist);
2026 MaybeSavePersistentMiscAttributes(aAttributes, *docShellElement,
2027 persistString, shouldPersist);
2028 mPersistentAttributesDirty -= aAttributes;
2031 NS_IMETHODIMP AppWindow::GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow) {
2032 NS_ENSURE_STATE(mDocShell);
2034 if (!mDOMWindow) mDOMWindow = mDocShell->GetWindow();
2035 NS_ENSURE_TRUE(mDOMWindow, NS_ERROR_FAILURE);
2037 *aDOMWindow = mDOMWindow;
2038 NS_ADDREF(*aDOMWindow);
2039 return NS_OK;
2042 dom::Element* AppWindow::GetWindowDOMElement() const {
2043 NS_ENSURE_TRUE(mDocShell, nullptr);
2045 nsCOMPtr<nsIDocumentViewer> viewer;
2046 mDocShell->GetDocViewer(getter_AddRefs(viewer));
2047 NS_ENSURE_TRUE(viewer, nullptr);
2049 const dom::Document* document = viewer->GetDocument();
2050 NS_ENSURE_TRUE(document, nullptr);
2052 return document->GetRootElement();
2055 nsresult AppWindow::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
2056 bool aPrimary) {
2057 // Set the default content tree owner
2058 if (aPrimary) {
2059 NS_ENSURE_SUCCESS(EnsurePrimaryContentTreeOwner(), NS_ERROR_FAILURE);
2060 aContentShell->SetTreeOwner(mPrimaryContentTreeOwner);
2061 mPrimaryContentShell = aContentShell;
2062 mPrimaryBrowserParent = nullptr;
2063 } else {
2064 NS_ENSURE_SUCCESS(EnsureContentTreeOwner(), NS_ERROR_FAILURE);
2065 aContentShell->SetTreeOwner(mContentTreeOwner);
2066 if (mPrimaryContentShell == aContentShell) mPrimaryContentShell = nullptr;
2069 return NS_OK;
2072 nsresult AppWindow::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
2073 if (mPrimaryContentShell == aContentShell) {
2074 mPrimaryContentShell = nullptr;
2076 return NS_OK;
2079 NS_IMETHODIMP
2080 AppWindow::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
2081 if (mPrimaryBrowserParent) {
2082 return GetPrimaryRemoteTabSize(aWidth, aHeight);
2084 if (mPrimaryContentShell) {
2085 return GetPrimaryContentShellSize(aWidth, aHeight);
2087 return NS_ERROR_UNEXPECTED;
2090 nsresult AppWindow::GetPrimaryRemoteTabSize(int32_t* aWidth, int32_t* aHeight) {
2091 BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
2092 // Need strong ref, since Client* can run script.
2093 RefPtr<dom::Element> element = host->GetOwnerElement();
2094 NS_ENSURE_STATE(element);
2096 CSSIntSize size(element->ClientWidth(), element->ClientHeight());
2097 LayoutDeviceIntSize sizeDev =
2098 RoundedToInt(size * UnscaledDevicePixelsPerCSSPixel());
2099 if (aWidth) {
2100 *aWidth = sizeDev.width;
2102 if (aHeight) {
2103 *aHeight = sizeDev.height;
2105 return NS_OK;
2108 nsresult AppWindow::GetPrimaryContentShellSize(int32_t* aWidth,
2109 int32_t* aHeight) {
2110 NS_ENSURE_STATE(mPrimaryContentShell);
2112 nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(mPrimaryContentShell));
2113 NS_ENSURE_STATE(shellWindow);
2115 LayoutDeviceIntSize sizeDev = shellWindow->GetSize();
2116 if (aWidth) {
2117 *aWidth = sizeDev.width;
2119 if (aHeight) {
2120 *aHeight = sizeDev.height;
2122 return NS_OK;
2125 NS_IMETHODIMP
2126 AppWindow::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
2127 if (mPrimaryBrowserParent) {
2128 return SetPrimaryRemoteTabSize(aWidth, aHeight);
2130 if (mPrimaryContentShell) {
2131 return SizeShellTo(mPrimaryContentShell, aWidth, aHeight);
2133 return NS_ERROR_UNEXPECTED;
2136 nsresult AppWindow::SetPrimaryRemoteTabSize(int32_t aWidth, int32_t aHeight) {
2137 int32_t shellWidth, shellHeight;
2138 GetPrimaryRemoteTabSize(&shellWidth, &shellHeight);
2139 SizeShellToWithLimit(aWidth, aHeight, shellWidth, shellHeight);
2140 return NS_OK;
2143 nsresult AppWindow::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
2144 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
2145 return mDocShell->GetSize(aWidth, aHeight);
2148 nsresult AppWindow::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
2149 return SizeShellTo(mDocShell, aWidth, aHeight);
2152 NS_IMETHODIMP AppWindow::SizeShellTo(nsIDocShellTreeItem* aShellItem,
2153 int32_t aCX, int32_t aCY) {
2154 MOZ_ASSERT(aShellItem == mDocShell || aShellItem == mPrimaryContentShell);
2155 if (aShellItem == mDocShell) {
2156 auto newSize =
2157 LayoutDeviceIntSize(aCX, aCY) + GetOuterToInnerSizeDifference(mWindow);
2158 SetSize(newSize.width, newSize.height, /* aRepaint = */ true);
2159 mDominantClientSize = true;
2160 return NS_OK;
2163 // XXXTAB This is wrong, we should actually reflow based on the passed in
2164 // shell. For now we are hacking and doing delta sizing. This is bad
2165 // because it assumes all size we add will go to the shell which probably
2166 // won't happen.
2167 nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
2168 NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
2170 int32_t width = 0;
2171 int32_t height = 0;
2172 shellAsWin->GetSize(&width, &height);
2174 SizeShellToWithLimit(aCX, aCY, width, height);
2176 return NS_OK;
2179 NS_IMETHODIMP AppWindow::ExitModalLoop(nsresult aStatus) {
2180 if (mContinueModalLoop) EnableParent(true);
2181 mContinueModalLoop = false;
2182 mModalStatus = aStatus;
2183 return NS_OK;
2186 // top-level function to create a new window
2187 NS_IMETHODIMP AppWindow::CreateNewWindow(int32_t aChromeFlags,
2188 nsIOpenWindowInfo* aOpenWindowInfo,
2189 nsIAppWindow** _retval) {
2190 NS_ENSURE_ARG_POINTER(_retval);
2192 if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME) {
2193 MOZ_RELEASE_ASSERT(
2194 !aOpenWindowInfo,
2195 "Unexpected nsOpenWindowInfo when creating a new chrome window");
2196 return CreateNewChromeWindow(aChromeFlags, _retval);
2199 return CreateNewContentWindow(aChromeFlags, aOpenWindowInfo, _retval);
2202 NS_IMETHODIMP AppWindow::CreateNewChromeWindow(int32_t aChromeFlags,
2203 nsIAppWindow** _retval) {
2204 nsCOMPtr<nsIAppShellService> appShell(
2205 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
2206 NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
2208 // Just do a normal create of a window and return.
2209 nsCOMPtr<nsIAppWindow> newWindow;
2210 appShell->CreateTopLevelWindow(
2211 this, nullptr, aChromeFlags, nsIAppShellService::SIZE_TO_CONTENT,
2212 nsIAppShellService::SIZE_TO_CONTENT, getter_AddRefs(newWindow));
2214 NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
2216 newWindow.forget(_retval);
2218 return NS_OK;
2221 NS_IMETHODIMP AppWindow::CreateNewContentWindow(
2222 int32_t aChromeFlags, nsIOpenWindowInfo* aOpenWindowInfo,
2223 nsIAppWindow** _retval) {
2224 nsCOMPtr<nsIAppShellService> appShell(
2225 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
2226 NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
2228 // We need to create a new top level window and then enter a nested
2229 // loop. Eventually the new window will be told that it has loaded,
2230 // at which time we know it is safe to spin out of the nested loop
2231 // and allow the opening code to proceed.
2233 nsCOMPtr<nsIURI> uri;
2234 nsAutoCString urlStr;
2235 urlStr.AssignLiteral(BROWSER_CHROME_URL_QUOTED);
2237 nsCOMPtr<nsIIOService> service(do_GetService(NS_IOSERVICE_CONTRACTID));
2238 if (service) {
2239 service->NewURI(urlStr, nullptr, nullptr, getter_AddRefs(uri));
2241 NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
2243 // We need to create a chrome window to contain the content window we're about
2244 // to pass back. The subject principal needs to be system while we're creating
2245 // it to make things work right, so force a system caller. See bug 799348
2246 // comment 13 for a description of what happens when we don't.
2247 nsCOMPtr<nsIAppWindow> newWindow;
2249 AutoNoJSAPI nojsapi;
2250 appShell->CreateTopLevelWindow(this, uri, aChromeFlags, 615, 480,
2251 getter_AddRefs(newWindow));
2252 NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
2255 AppWindow* appWin =
2256 static_cast<AppWindow*>(static_cast<nsIAppWindow*>(newWindow));
2258 // Specify which flags should be used by browser.xhtml to create the initial
2259 // content browser window.
2260 appWin->mInitialOpenWindowInfo = aOpenWindowInfo;
2262 // Specify that we want the window to remain locked until the chrome has
2263 // loaded.
2264 appWin->LockUntilChromeLoad();
2267 AutoNoJSAPI nojsapi;
2268 SpinEventLoopUntil("AppWindow::CreateNewContentWindow"_ns,
2269 [&]() { return !appWin->IsLocked(); });
2272 NS_ENSURE_STATE(appWin->mPrimaryContentShell ||
2273 appWin->mPrimaryBrowserParent);
2274 MOZ_ASSERT_IF(appWin->mPrimaryContentShell,
2275 !aOpenWindowInfo->GetNextRemoteBrowser());
2277 newWindow.forget(_retval);
2279 return NS_OK;
2282 NS_IMETHODIMP AppWindow::GetHasPrimaryContent(bool* aResult) {
2283 *aResult = mPrimaryBrowserParent || mPrimaryContentShell;
2284 return NS_OK;
2287 void AppWindow::EnableParent(bool aEnable) {
2288 nsCOMPtr<nsIBaseWindow> parentWindow;
2289 nsCOMPtr<nsIWidget> parentWidget;
2291 parentWindow = do_QueryReferent(mParentWindow);
2292 if (parentWindow) parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
2293 if (parentWidget) parentWidget->Enable(aEnable);
2296 void AppWindow::SetContentScrollbarVisibility(bool aVisible) {
2297 nsCOMPtr<nsPIDOMWindowOuter> contentWin(
2298 do_GetInterface(mPrimaryContentShell));
2299 if (!contentWin) {
2300 return;
2303 nsContentUtils::SetScrollbarsVisibility(contentWin->GetDocShell(), aVisible);
2306 void AppWindow::ApplyChromeFlags() {
2307 nsCOMPtr<dom::Element> window = GetWindowDOMElement();
2308 if (!window) {
2309 return;
2312 if (mChromeLoaded) {
2313 // The two calls in this block don't need to happen early because they
2314 // don't cause a global restyle on the document. Not only that, but the
2315 // scrollbar stuff needs a content area to toggle the scrollbars on anyway.
2316 // So just don't do these until mChromeLoaded is true.
2318 // Scrollbars have their own special treatment.
2319 SetContentScrollbarVisibility(mChromeFlags &
2320 nsIWebBrowserChrome::CHROME_SCROLLBARS);
2323 /* the other flags are handled together. we have style rules
2324 in navigator.css that trigger visibility based on
2325 the 'chromehidden' attribute of the <window> tag. */
2326 nsAutoString newvalue;
2328 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_MENUBAR))
2329 newvalue.AppendLiteral("menubar ");
2331 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_TOOLBAR))
2332 newvalue.AppendLiteral("toolbar ");
2334 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_LOCATIONBAR))
2335 newvalue.AppendLiteral("location ");
2337 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR))
2338 newvalue.AppendLiteral("directories ");
2340 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_STATUSBAR))
2341 newvalue.AppendLiteral("status ");
2343 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_EXTRA))
2344 newvalue.AppendLiteral("extrachrome ");
2346 // Note that if we're not actually changing the value this will be a no-op,
2347 // so no need to compare to the old value.
2348 IgnoredErrorResult rv;
2349 window->SetAttribute(u"chromehidden"_ns, newvalue, rv);
2352 NS_IMETHODIMP
2353 AppWindow::BeforeStartLayout() {
2354 ApplyChromeFlags();
2355 // Ordering here is important, loading width/height values in
2356 // LoadPersistentWindowState() depends on the customtitlebar attribute (since
2357 // we need to translate outer to inner sizes).
2358 SyncAttributesToWidget();
2359 LoadPersistentWindowState();
2360 if (mWindow) {
2361 SizeShell();
2363 return NS_OK;
2366 NS_IMETHODIMP
2367 AppWindow::LockAspectRatio(bool aShouldLock) {
2368 mWindow->LockAspectRatio(aShouldLock);
2369 return NS_OK;
2372 NS_IMETHODIMP
2373 AppWindow::NeedFastSnaphot() {
2374 MOZ_ASSERT(mWindow);
2375 if (!mWindow) {
2376 return NS_ERROR_FAILURE;
2378 mWindow->SetNeedFastSnaphot();
2379 return NS_OK;
2382 void AppWindow::LoadPersistentWindowState() {
2383 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
2384 if (!docShellElement) {
2385 return;
2388 // Check if the window wants to persist anything.
2389 nsAutoString persist;
2390 docShellElement->GetAttr(nsGkAtoms::persist, persist);
2391 if (persist.IsEmpty()) {
2392 return;
2395 auto loadValue = [&](nsAtom* aAttr) {
2396 nsDependentAtomString attrString(aAttr);
2397 if (persist.Find(attrString) >= 0) {
2398 nsAutoString value;
2399 nsresult rv = GetPersistentValue(aAttr, value);
2400 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get persistent state.");
2401 if (NS_SUCCEEDED(rv) && !value.IsEmpty()) {
2402 docShellElement->SetAttr(aAttr, value, IgnoreErrors());
2407 loadValue(nsGkAtoms::screenX);
2408 loadValue(nsGkAtoms::screenY);
2409 loadValue(nsGkAtoms::width);
2410 loadValue(nsGkAtoms::height);
2411 loadValue(nsGkAtoms::sizemode);
2414 void AppWindow::IntrinsicallySizeShell(const CSSIntSize& aWindowDiff,
2415 int32_t& aSpecWidth,
2416 int32_t& aSpecHeight) {
2417 nsCOMPtr<nsIDocumentViewer> viewer;
2418 mDocShell->GetDocViewer(getter_AddRefs(viewer));
2419 if (!viewer) {
2420 return;
2422 RefPtr<nsDocShell> docShell = mDocShell;
2424 CSSIntCoord maxWidth = 0;
2425 CSSIntCoord maxHeight = 0;
2426 CSSIntCoord prefWidth = 0;
2427 if (RefPtr element = GetWindowDOMElement()) {
2428 nsAutoString prefWidthAttr;
2429 if (element->GetAttr(nsGkAtoms::prefwidth, prefWidthAttr)) {
2430 // TODO: Make this more generic perhaps?
2431 if (prefWidthAttr.EqualsLiteral("min-width")) {
2432 if (auto* f = element->GetPrimaryFrame(FlushType::Frames)) {
2433 const auto& coord = f->StylePosition()->GetMinWidth();
2434 if (coord.ConvertsToLength()) {
2435 prefWidth = CSSPixel::FromAppUnitsRounded(coord.ToLength());
2442 Maybe<CSSIntSize> size =
2443 viewer->GetContentSize(maxWidth, maxHeight, prefWidth);
2444 if (!size) {
2445 return;
2447 nsPresContext* pc = viewer->GetPresContext();
2448 MOZ_ASSERT(pc, "Should have pres context");
2450 int32_t width = pc->CSSPixelsToDevPixels(size->width);
2451 int32_t height = pc->CSSPixelsToDevPixels(size->height);
2452 SizeShellTo(docShell, width, height);
2454 // Update specified size for the final LoadPositionFromXUL call.
2455 aSpecWidth = size->width + aWindowDiff.width;
2456 aSpecHeight = size->height + aWindowDiff.height;
2459 void AppWindow::SizeShell() {
2460 AutoRestore<bool> sizingShellFromXUL(mSizingShellFromXUL);
2461 mSizingShellFromXUL = true;
2463 int32_t specWidth = -1, specHeight = -1;
2464 bool gotSize = false;
2466 nsAutoString windowType;
2467 if (nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement()) {
2468 windowElement->GetAttr(nsGkAtoms::windowtype, windowType);
2471 const CSSIntSize windowDiff = GetOuterToInnerSizeDifferenceInCSSPixels(
2472 mWindow, UnscaledDevicePixelsPerCSSPixel());
2474 // If we're using fingerprint resistance, we're going to resize the window
2475 // once we have primary content.
2476 if (nsContentUtils::ShouldResistFingerprinting(
2477 "if RFP is enabled we want to round the dimensions of the new"
2478 "new pop up window regardless of their origin",
2479 RFPTarget::RoundWindowSize) &&
2480 windowType.EqualsLiteral("navigator:browser")) {
2481 // Once we've got primary content, force dimensions.
2482 if (mPrimaryContentShell || mPrimaryBrowserParent) {
2483 ForceRoundedDimensions();
2485 // Always avoid setting size/sizemode on this window.
2486 mIgnoreXULSize = true;
2487 mIgnoreXULSizeMode = true;
2488 } else if (!mIgnoreXULSize) {
2489 gotSize = LoadSizeFromXUL(specWidth, specHeight);
2490 specWidth += windowDiff.width;
2491 specHeight += windowDiff.height;
2494 bool positionSet = !mIgnoreXULPosition;
2495 nsCOMPtr<nsIAppWindow> parentWindow(do_QueryReferent(mParentWindow));
2496 #if defined(XP_UNIX) && !defined(XP_MACOSX)
2497 // don't override WM placement on unix for independent, top-level windows
2498 // (however, we think the benefits of intelligent dependent window placement
2499 // trump that override.)
2500 if (!parentWindow) positionSet = false;
2501 #endif
2502 if (positionSet) {
2503 // We have to do this before sizing the window, because sizing depends
2504 // on the resolution of the screen we're on. But positioning needs to
2505 // know the size so that it can constrain to screen bounds.... as an
2506 // initial guess here, we'll use the specified size (if any).
2507 positionSet = LoadPositionFromXUL(specWidth, specHeight);
2510 if (gotSize) {
2511 SetSpecifiedSize(specWidth, specHeight);
2514 // If LoadSizeFromXUL set the size, mIntrinsicallySized will be false.
2515 if (mIntrinsicallySized) {
2516 IntrinsicallySizeShell(windowDiff, specWidth, specHeight);
2519 // Now that we have set the window's final size, we can re-do its
2520 // positioning so that it is properly constrained to the screen.
2521 if (positionSet) {
2522 LoadPositionFromXUL(specWidth, specHeight);
2525 UpdateWindowStateFromMiscXULAttributes();
2527 if (mChromeLoaded && mCenterAfterLoad && !positionSet &&
2528 mWindow->SizeMode() == nsSizeMode_Normal) {
2529 Center(parentWindow, parentWindow ? false : true, false);
2533 NS_IMETHODIMP AppWindow::GetXULBrowserWindow(
2534 nsIXULBrowserWindow** aXULBrowserWindow) {
2535 NS_IF_ADDREF(*aXULBrowserWindow = mXULBrowserWindow);
2536 return NS_OK;
2539 NS_IMETHODIMP AppWindow::SetXULBrowserWindow(
2540 nsIXULBrowserWindow* aXULBrowserWindow) {
2541 mXULBrowserWindow = aXULBrowserWindow;
2542 return NS_OK;
2545 // Given the dimensions of some content area held within this XUL window, and
2546 // assuming that that content area will change its dimensions in linear
2547 // proportion to the dimensions of this XUL window, changes the size of the XUL
2548 // window so that the content area reaches a particular size.
2549 void AppWindow::SizeShellToWithLimit(int32_t aDesiredWidth,
2550 int32_t aDesiredHeight,
2551 int32_t shellItemWidth,
2552 int32_t shellItemHeight) {
2553 int32_t widthDelta = aDesiredWidth - shellItemWidth;
2554 int32_t heightDelta = aDesiredHeight - shellItemHeight;
2556 int32_t winWidth = 0;
2557 int32_t winHeight = 0;
2559 GetSize(&winWidth, &winHeight);
2560 // There's no point in trying to make the window smaller than the
2561 // desired content area size --- that's not likely to work. This whole
2562 // function assumes that the outer docshell is adding some constant
2563 // "border" chrome to the content area.
2564 winWidth = std::max(winWidth + widthDelta, aDesiredWidth);
2565 winHeight = std::max(winHeight + heightDelta, aDesiredHeight);
2567 // Note: Because of the asynchronous resizing on Linux we have to call
2568 // SetSize even when the size doesn't appear to change. A previous call that
2569 // has yet to complete can still change the size. We want the latest call to
2570 // define the final size.
2571 SetSize(winWidth, winHeight, true);
2572 mDominantClientSize = true;
2575 nsresult AppWindow::GetTabCount(uint32_t* aResult) {
2576 if (mXULBrowserWindow) {
2577 return mXULBrowserWindow->GetTabCount(aResult);
2580 *aResult = 0;
2581 return NS_OK;
2584 nsresult AppWindow::GetInitialOpenWindowInfo(
2585 nsIOpenWindowInfo** aOpenWindowInfo) {
2586 NS_ENSURE_ARG_POINTER(aOpenWindowInfo);
2587 *aOpenWindowInfo = do_AddRef(mInitialOpenWindowInfo).take();
2588 return NS_OK;
2591 PresShell* AppWindow::GetPresShell() {
2592 if (!mDocShell) {
2593 return nullptr;
2595 return mDocShell->GetPresShell();
2598 bool AppWindow::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) {
2599 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
2600 if (pm) {
2601 nsCOMPtr<nsPIDOMWindowOuter> window =
2602 mDocShell ? mDocShell->GetWindow() : nullptr;
2603 pm->AdjustPopupsOnWindowChange(window);
2606 // Notify all tabs that the widget moved.
2607 if (mDocShell && mDocShell->GetWindow()) {
2608 nsCOMPtr<EventTarget> eventTarget =
2609 mDocShell->GetWindow()->GetTopWindowRoot();
2610 nsContentUtils::DispatchChromeEvent(
2611 mDocShell->GetDocument(), eventTarget, u"MozUpdateWindowPos"_ns,
2612 CanBubble::eNo, Cancelable::eNo, nullptr);
2615 // Persist position, but not immediately, in case this OS is firing
2616 // repeated move events as the user drags the window
2617 PersistentAttributesDirty(PersistentAttribute::Position, Async);
2618 return false;
2621 bool AppWindow::WindowResized(nsIWidget* aWidget, int32_t aWidth,
2622 int32_t aHeight) {
2623 mDominantClientSize = false;
2624 if (mDocShell) {
2625 mDocShell->SetPositionAndSize(0, 0, aWidth, aHeight, 0);
2627 // Persist size, but not immediately, in case this OS is firing
2628 // repeated size events as the user drags the sizing handle
2629 if (!IsLocked()) {
2630 PersistentAttributesDirty(AllPersistentAttributes(), Async);
2632 // Check if we need to continue a fullscreen change.
2633 switch (mFullscreenChangeState) {
2634 case FullscreenChangeState::WillChange:
2635 mFullscreenChangeState = FullscreenChangeState::WidgetResized;
2636 break;
2637 case FullscreenChangeState::WidgetEnteredFullscreen:
2638 FinishFullscreenChange(true);
2639 break;
2640 case FullscreenChangeState::WidgetExitedFullscreen:
2641 FinishFullscreenChange(false);
2642 break;
2643 case FullscreenChangeState::WidgetResized:
2644 case FullscreenChangeState::NotChanging:
2645 break;
2647 return true;
2650 bool AppWindow::RequestWindowClose(nsIWidget* aWidget) {
2651 // Maintain a reference to this as it is about to get destroyed.
2652 nsCOMPtr<nsIAppWindow> appWindow(this);
2654 nsCOMPtr<nsPIDOMWindowOuter> window(mDocShell ? mDocShell->GetWindow()
2655 : nullptr);
2656 nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window);
2658 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
2659 if (!presShell) {
2660 mozilla::DebugOnly<bool> dying;
2661 MOZ_ASSERT(NS_SUCCEEDED(mDocShell->IsBeingDestroyed(&dying)) && dying,
2662 "No presShell, but window is not being destroyed");
2663 } else if (eventTarget) {
2664 RefPtr<nsPresContext> presContext = presShell->GetPresContext();
2666 nsEventStatus status = nsEventStatus_eIgnore;
2667 WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
2668 if (NS_SUCCEEDED(EventDispatcher::Dispatch(eventTarget, presContext, &event,
2669 nullptr, &status)) &&
2670 status == nsEventStatus_eConsumeNoDefault)
2671 return false;
2674 Destroy();
2675 return false;
2678 void AppWindow::SizeModeChanged(nsSizeMode aSizeMode) {
2679 const bool wasWidgetInFullscreen = mIsWidgetInFullscreen;
2680 // Fullscreen and minimized states are usually compatible, and the widget
2681 // typically returns to fullscreen after restoration. By not updating the
2682 // widget's fullscreen state while it is minimized, we can avoid unnecessary
2683 // fullscreen exits, such as those encountered in bug 1823284.
2684 if (aSizeMode != nsSizeMode_Minimized) {
2685 mIsWidgetInFullscreen = aSizeMode == nsSizeMode_Fullscreen;
2688 const bool fullscreenChanged = wasWidgetInFullscreen != mIsWidgetInFullscreen;
2689 if (fullscreenChanged) {
2690 FullscreenWillChange(mIsWidgetInFullscreen);
2693 RecomputeBrowsingContextVisibility();
2695 PersistentAttributesDirty(PersistentAttribute::Misc, Sync);
2696 nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
2697 mDocShell ? mDocShell->GetWindow() : nullptr;
2698 if (ourWindow) {
2699 // Always fire a user-defined sizemodechange event on the window
2700 ourWindow->DispatchCustomEvent(u"sizemodechange"_ns);
2703 if (PresShell* presShell = GetPresShell()) {
2704 presShell->GetPresContext()->SizeModeChanged(aSizeMode);
2707 if (fullscreenChanged) {
2708 FullscreenChanged(mIsWidgetInFullscreen);
2711 // Note the current implementation of SetSizeMode just stores
2712 // the new state; it doesn't actually resize. So here we store
2713 // the state and pass the event on to the OS. The day is coming
2714 // when we'll handle the event here, and the return result will
2715 // then need to be different.
2718 void AppWindow::FullscreenWillChange(bool aInFullscreen) {
2719 if (mDocShell) {
2720 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2721 ourWindow->FullscreenWillChange(aInFullscreen);
2724 MOZ_ASSERT(mFullscreenChangeState == FullscreenChangeState::NotChanging);
2726 CSSToLayoutDeviceScale scale = UnscaledDevicePixelsPerCSSPixel();
2727 CSSIntSize windowSizeCSS = RoundedToInt(GetSize() / scale);
2729 CSSIntSize screenSizeCSS;
2730 GetAvailScreenSize(&screenSizeCSS.width, &screenSizeCSS.height);
2732 // Check if the window is already at the expected dimensions. If it is, set
2733 // the fullscreen change state to WidgetResized to avoid waiting for a resize
2734 // event. On macOS, a fullscreen window could be slightly higher than
2735 // available screen size because of the OS menu bar isn't yet hidden.
2736 mFullscreenChangeState =
2737 (aInFullscreen == (windowSizeCSS.width == screenSizeCSS.width &&
2738 windowSizeCSS.height >= screenSizeCSS.height))
2739 ? FullscreenChangeState::WidgetResized
2740 : FullscreenChangeState::WillChange;
2743 void AppWindow::FullscreenChanged(bool aInFullscreen) {
2744 if (mFullscreenChangeState == FullscreenChangeState::WidgetResized) {
2745 FinishFullscreenChange(aInFullscreen);
2746 } else {
2747 NS_WARNING_ASSERTION(
2748 mFullscreenChangeState == FullscreenChangeState::WillChange,
2749 "Unexpected fullscreen change state");
2750 FullscreenChangeState newState =
2751 aInFullscreen ? FullscreenChangeState::WidgetEnteredFullscreen
2752 : FullscreenChangeState::WidgetExitedFullscreen;
2753 mFullscreenChangeState = newState;
2754 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
2755 // Wait for resize for a small amount of time.
2756 // 80ms is actually picked arbitrarily. But it shouldn't be too large
2757 // in case the widget resize is not going to happen at all, which can
2758 // be the case for some Linux window managers and possibly Android.
2759 NS_DelayedDispatchToCurrentThread(
2760 NS_NewRunnableFunction(
2761 "AppWindow::FullscreenChanged",
2762 [this, kungFuDeathGrip, newState, aInFullscreen]() {
2763 if (mFullscreenChangeState == newState) {
2764 FinishFullscreenChange(aInFullscreen);
2767 80);
2771 void AppWindow::FinishFullscreenChange(bool aInFullscreen) {
2772 mFullscreenChangeState = FullscreenChangeState::NotChanging;
2773 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
2774 pm->Rollup({});
2776 if (mDocShell) {
2777 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2778 ourWindow->FinishFullscreenChange(aInFullscreen);
2783 void AppWindow::MacFullscreenMenubarOverlapChanged(
2784 mozilla::DesktopCoord aOverlapAmount) {
2785 if (mDocShell) {
2786 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2787 ourWindow->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
2792 void AppWindow::RecomputeBrowsingContextVisibility() {
2793 if (!mDocShell) {
2794 return;
2796 RefPtr bc = mDocShell->GetBrowsingContext();
2797 if (!bc) {
2798 return;
2800 bc->Canonical()->RecomputeAppWindowVisibility();
2803 void AppWindow::OcclusionStateChanged(bool aIsFullyOccluded) {
2804 if (!mDocShell) {
2805 return;
2807 RecomputeBrowsingContextVisibility();
2808 if (RefPtr win = mDocShell->GetWindow()) {
2809 // And always fire a user-defined occlusionstatechange event on the window
2810 win->DispatchCustomEvent(u"occlusionstatechange"_ns,
2811 ChromeOnlyDispatch::eYes);
2815 void AppWindow::OSToolbarButtonPressed() {
2816 // Keep a reference as setting the chrome flags can fire events.
2817 nsCOMPtr<nsIAppWindow> appWindow(this);
2819 // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
2820 // due to components with multiple sidebar components
2821 // (such as Mail/News, Addressbook, etc)... and frankly,
2822 // Mac IE, OmniWeb, and other Mac OS X apps all work this way
2823 uint32_t chromeMask = (nsIWebBrowserChrome::CHROME_TOOLBAR |
2824 nsIWebBrowserChrome::CHROME_LOCATIONBAR |
2825 nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
2827 nsCOMPtr<nsIWebBrowserChrome> wbc(do_GetInterface(appWindow));
2828 if (!wbc) return;
2830 uint32_t chromeFlags, newChromeFlags = 0;
2831 wbc->GetChromeFlags(&chromeFlags);
2832 newChromeFlags = chromeFlags & chromeMask;
2833 if (!newChromeFlags)
2834 chromeFlags |= chromeMask;
2835 else
2836 chromeFlags &= (~newChromeFlags);
2837 wbc->SetChromeFlags(chromeFlags);
2840 void AppWindow::WindowActivated() {
2841 nsCOMPtr<nsIAppWindow> appWindow(this);
2843 // focusing the window could cause it to close, so keep a reference to it
2844 if (mDocShell) {
2845 if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
2846 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
2847 fm->WindowRaised(window, nsFocusManager::GenerateFocusActionId());
2852 if (mChromeLoaded) {
2853 PersistentAttributesDirty(AllPersistentAttributes(), Sync);
2857 void AppWindow::WindowDeactivated() {
2858 if (mDocShell) {
2859 if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
2860 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
2861 if (!fm->IsTestMode()) {
2862 fm->WindowLowered(window, nsFocusManager::GenerateFocusActionId());
2869 #ifdef USE_NATIVE_MENUS
2871 struct LoadNativeMenusListener {
2872 LoadNativeMenusListener(Document* aDoc, nsIWidget* aParentWindow)
2873 : mDocument(aDoc), mParentWindow(aParentWindow) {}
2875 RefPtr<Document> mDocument;
2876 nsCOMPtr<nsIWidget> mParentWindow;
2879 // On macOS the hidden window is created eagerly, and we want to wait for it to
2880 // load the native menus.
2881 static bool sWaitingForHiddenWindowToLoadNativeMenus =
2882 # ifdef XP_MACOSX
2883 true
2884 # else
2885 false
2886 # endif
2889 MOZ_RUNINIT static nsTArray<LoadNativeMenusListener> sLoadNativeMenusListeners;
2891 static void BeginLoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow);
2893 static void LoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
2894 MOZ_ASSERT(!gfxPlatform::IsHeadless());
2896 // Find the menubar tag (if there is more than one, we ignore all but
2897 // the first).
2898 nsCOMPtr<nsINodeList> menubarElements = aDoc->GetElementsByTagNameNS(
2899 u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns,
2900 u"menubar"_ns);
2902 RefPtr<Element> menubar;
2903 if (menubarElements) {
2904 menubar = Element::FromNodeOrNull(menubarElements->Item(0));
2907 widget::NativeMenuSupport::CreateNativeMenuBar(aParentWindow, menubar);
2909 if (sWaitingForHiddenWindowToLoadNativeMenus) {
2910 sWaitingForHiddenWindowToLoadNativeMenus = false;
2911 for (auto& listener : sLoadNativeMenusListeners) {
2912 BeginLoadNativeMenus(listener.mDocument, listener.mParentWindow);
2914 sLoadNativeMenusListeners.Clear();
2918 class L10nReadyPromiseHandler final : public dom::PromiseNativeHandler {
2919 public:
2920 NS_DECL_ISUPPORTS
2922 L10nReadyPromiseHandler(Document* aDoc, nsIWidget* aParentWindow)
2923 : mDocument(aDoc), mWindow(aParentWindow) {}
2925 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
2926 ErrorResult& aRv) override {
2927 LoadNativeMenus(mDocument, mWindow);
2930 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
2931 ErrorResult& aRv) override {
2932 // Again, this shouldn't happen, but fallback to loading the menus as is.
2933 NS_WARNING(
2934 "L10nReadyPromiseHandler rejected - loading fallback native "
2935 "menu.");
2936 LoadNativeMenus(mDocument, mWindow);
2939 private:
2940 ~L10nReadyPromiseHandler() = default;
2942 RefPtr<Document> mDocument;
2943 nsCOMPtr<nsIWidget> mWindow;
2946 NS_IMPL_ISUPPORTS0(L10nReadyPromiseHandler)
2948 static void BeginLoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
2949 if (RefPtr<DocumentL10n> l10n = aDoc->GetL10n()) {
2950 // Wait for l10n to be ready so the menus are localized.
2951 RefPtr<Promise> promise = l10n->Ready();
2952 MOZ_ASSERT(promise);
2953 RefPtr handler = new L10nReadyPromiseHandler(aDoc, aParentWindow);
2954 promise->AppendNativeHandler(handler);
2955 } else {
2956 // Something went wrong loading the doc and l10n wasn't created. This
2957 // shouldn't really happen, but if it does fallback to trying to load
2958 // the menus as is.
2959 LoadNativeMenus(aDoc, aParentWindow);
2963 #endif
2965 class AppWindowTimerCallback final : public nsITimerCallback, public nsINamed {
2966 public:
2967 explicit AppWindowTimerCallback(AppWindow* aWindow) : mWindow(aWindow) {}
2969 NS_DECL_THREADSAFE_ISUPPORTS
2971 NS_IMETHOD Notify(nsITimer* aTimer) override {
2972 // Although this object participates in a refcount cycle (this -> mWindow
2973 // -> mSPTimer -> this), mSPTimer is a one-shot timer and releases this
2974 // after it fires. So we don't need to release mWindow here.
2976 mWindow->FirePersistenceTimer();
2977 return NS_OK;
2980 NS_IMETHOD GetName(nsACString& aName) override {
2981 aName.AssignLiteral("AppWindowTimerCallback");
2982 return NS_OK;
2985 private:
2986 ~AppWindowTimerCallback() {}
2988 RefPtr<AppWindow> mWindow;
2991 NS_IMPL_ISUPPORTS(AppWindowTimerCallback, nsITimerCallback, nsINamed)
2993 void AppWindow::PersistentAttributesDirty(PersistentAttributes aAttributes,
2994 PersistentAttributeUpdate aUpdate) {
2995 aAttributes = aAttributes & mPersistentAttributesMask;
2996 if (aAttributes.isEmpty()) {
2997 return;
3000 mPersistentAttributesDirty += aAttributes;
3001 if (aUpdate == Sync) {
3002 // Only apply the attributes we've been requested to apply sync, not other
3003 // potentially dirty attributes that have been requested asynchronously.
3004 SavePersistentAttributes(aAttributes);
3005 return;
3007 if (!mSPTimer) {
3008 mSPTimer = NS_NewTimer();
3009 if (!mSPTimer) {
3010 NS_WARNING("Couldn't create timer instance?");
3011 return;
3015 RefPtr<AppWindowTimerCallback> callback = new AppWindowTimerCallback(this);
3016 mSPTimer->InitWithCallback(callback, SIZE_PERSISTENCE_TIMEOUT,
3017 nsITimer::TYPE_ONE_SHOT);
3020 void AppWindow::FirePersistenceTimer() { SavePersistentAttributes(); }
3022 //----------------------------------------
3023 // nsIWebProgessListener implementation
3024 //----------------------------------------
3025 NS_IMETHODIMP
3026 AppWindow::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3027 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
3028 int32_t aCurTotalProgress,
3029 int32_t aMaxTotalProgress) {
3030 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3031 return NS_OK;
3034 NS_IMETHODIMP
3035 AppWindow::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3036 uint32_t aStateFlags, nsresult aStatus) {
3037 // If the notification is not about a document finishing, then just
3038 // ignore it...
3039 if (!(aStateFlags & nsIWebProgressListener::STATE_STOP) ||
3040 !(aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) {
3041 return NS_OK;
3044 if (mChromeLoaded) return NS_OK;
3046 // If this document notification is for a frame then ignore it...
3047 nsCOMPtr<mozIDOMWindowProxy> eventWin;
3048 aProgress->GetDOMWindow(getter_AddRefs(eventWin));
3049 auto* eventPWin = nsPIDOMWindowOuter::From(eventWin);
3050 if (eventPWin) {
3051 nsPIDOMWindowOuter* rootPWin = eventPWin->GetPrivateRoot();
3052 if (eventPWin != rootPWin) return NS_OK;
3055 mChromeLoaded = true;
3056 mLockedUntilChromeLoad = false;
3058 #ifdef USE_NATIVE_MENUS
3059 ///////////////////////////////
3060 // Find the Menubar DOM and Load the menus, hooking them up to the loaded
3061 // commands
3062 ///////////////////////////////
3063 if (!gfxPlatform::IsHeadless()) {
3064 if (RefPtr<Document> menubarDoc = mDocShell->GetExtantDocument()) {
3065 if (mIsHiddenWindow || !sWaitingForHiddenWindowToLoadNativeMenus) {
3066 BeginLoadNativeMenus(menubarDoc, mWindow);
3067 } else {
3068 sLoadNativeMenusListeners.EmplaceBack(menubarDoc, mWindow);
3072 #endif // USE_NATIVE_MENUS
3074 OnChromeLoaded();
3076 return NS_OK;
3079 NS_IMETHODIMP
3080 AppWindow::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3081 nsIURI* aURI, uint32_t aFlags) {
3082 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3083 return NS_OK;
3086 NS_IMETHODIMP
3087 AppWindow::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3088 nsresult aStatus, const char16_t* aMessage) {
3089 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3090 return NS_OK;
3093 NS_IMETHODIMP
3094 AppWindow::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3095 uint32_t aState) {
3096 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3097 return NS_OK;
3100 NS_IMETHODIMP
3101 AppWindow::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
3102 nsIRequest* aRequest, uint32_t aEvent) {
3103 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3104 return NS_OK;
3108 * ExecuteCloseHandler - Run the close handler, if any.
3109 * @return true iff we found a close handler to run.
3111 bool AppWindow::ExecuteCloseHandler() {
3112 /* If the event handler closes this window -- a likely scenario --
3113 things get deleted out of order without this death grip.
3114 (The problem may be the death grip in nsWindow::windowProc,
3115 which forces this window's widget to remain alive longer
3116 than it otherwise would.) */
3117 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
3119 nsCOMPtr<EventTarget> eventTarget;
3120 if (mDocShell) {
3121 eventTarget = do_QueryInterface(mDocShell->GetWindow());
3124 if (eventTarget) {
3125 nsCOMPtr<nsIDocumentViewer> viewer;
3126 mDocShell->GetDocViewer(getter_AddRefs(viewer));
3127 if (viewer) {
3128 RefPtr<nsPresContext> presContext = viewer->GetPresContext();
3130 nsEventStatus status = nsEventStatus_eIgnore;
3131 WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
3133 nsresult rv = EventDispatcher::Dispatch(eventTarget, presContext, &event,
3134 nullptr, &status);
3135 if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault)
3136 return true;
3137 // else fall through and return false
3141 return false;
3142 } // ExecuteCloseHandler
3144 void AppWindow::ConstrainToOpenerScreen(int32_t* aX, int32_t* aY) {
3145 if (mOpenerScreenRect.IsEmpty()) {
3146 *aX = *aY = 0;
3147 return;
3150 int32_t left, top, width, height;
3151 // Constrain initial positions to the same screen as opener
3152 nsCOMPtr<nsIScreenManager> screenmgr =
3153 do_GetService("@mozilla.org/gfx/screenmanager;1");
3154 if (screenmgr) {
3155 nsCOMPtr<nsIScreen> screen = screenmgr->ScreenForRect(mOpenerScreenRect);
3156 if (screen) {
3157 screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
3158 if (*aX < left || *aX > left + width) {
3159 *aX = left;
3161 if (*aY < top || *aY > top + height) {
3162 *aY = top;
3168 nsIAppWindow* AppWindow::WidgetListenerDelegate::GetAppWindow() {
3169 return mAppWindow->GetAppWindow();
3172 PresShell* AppWindow::WidgetListenerDelegate::GetPresShell() {
3173 return mAppWindow->GetPresShell();
3176 bool AppWindow::WidgetListenerDelegate::WindowMoved(nsIWidget* aWidget,
3177 int32_t aX, int32_t aY,
3178 ByMoveToRect) {
3179 RefPtr<AppWindow> holder = mAppWindow;
3180 return holder->WindowMoved(aWidget, aX, aY);
3183 bool AppWindow::WidgetListenerDelegate::WindowResized(nsIWidget* aWidget,
3184 int32_t aWidth,
3185 int32_t aHeight) {
3186 RefPtr<AppWindow> holder = mAppWindow;
3187 return holder->WindowResized(aWidget, aWidth, aHeight);
3190 bool AppWindow::WidgetListenerDelegate::RequestWindowClose(nsIWidget* aWidget) {
3191 RefPtr<AppWindow> holder = mAppWindow;
3192 return holder->RequestWindowClose(aWidget);
3195 void AppWindow::WidgetListenerDelegate::SizeModeChanged(nsSizeMode aSizeMode) {
3196 RefPtr<AppWindow> holder = mAppWindow;
3197 holder->SizeModeChanged(aSizeMode);
3200 void AppWindow::WidgetListenerDelegate::MacFullscreenMenubarOverlapChanged(
3201 DesktopCoord aOverlapAmount) {
3202 RefPtr<AppWindow> holder = mAppWindow;
3203 return holder->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
3206 void AppWindow::WidgetListenerDelegate::OcclusionStateChanged(
3207 bool aIsFullyOccluded) {
3208 RefPtr<AppWindow> holder = mAppWindow;
3209 holder->OcclusionStateChanged(aIsFullyOccluded);
3212 void AppWindow::WidgetListenerDelegate::OSToolbarButtonPressed() {
3213 RefPtr<AppWindow> holder = mAppWindow;
3214 holder->OSToolbarButtonPressed();
3217 void AppWindow::WidgetListenerDelegate::WindowActivated() {
3218 RefPtr<AppWindow> holder = mAppWindow;
3219 holder->WindowActivated();
3222 void AppWindow::WidgetListenerDelegate::WindowDeactivated() {
3223 RefPtr<AppWindow> holder = mAppWindow;
3224 holder->WindowDeactivated();
3227 } // namespace mozilla