Bug 1914004 - Part 1: Add RootedTuple and RootedField to allow rooting multiple thing...
[gecko.git] / xpfe / appshell / AppWindow.cpp
blobe9bad0e92d12059d6120e6132cd452dfcbed2eea
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 "nsIWindowsUIUtils.h"
77 #endif
79 #include "mozilla/dom/DocumentL10n.h"
81 #if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
82 # include "mozilla/widget/NativeMenuSupport.h"
83 # define USE_NATIVE_MENUS
84 #endif
86 #define SIZEMODE_NORMAL u"normal"_ns
87 #define SIZEMODE_MAXIMIZED u"maximized"_ns
88 #define SIZEMODE_MINIMIZED u"minimized"_ns
89 #define SIZEMODE_FULLSCREEN u"fullscreen"_ns
91 #define SIZE_PERSISTENCE_TIMEOUT 500 // msec
93 //*****************************************************************************
94 //*** AppWindow: Object Management
95 //*****************************************************************************
97 namespace mozilla {
99 using dom::AutoNoJSAPI;
100 using dom::BrowserHost;
101 using dom::BrowsingContext;
102 using dom::Document;
103 using dom::DocumentL10n;
104 using dom::Element;
105 using dom::EventTarget;
106 using dom::LoadURIOptions;
107 using dom::Promise;
109 AppWindow::AppWindow(uint32_t aChromeFlags)
110 : mChromeTreeOwner(nullptr),
111 mContentTreeOwner(nullptr),
112 mPrimaryContentTreeOwner(nullptr),
113 mModalStatus(NS_OK),
114 mFullscreenChangeState(FullscreenChangeState::NotChanging),
115 mContinueModalLoop(false),
116 mDebuting(false),
117 mChromeLoaded(false),
118 mSizingShellFromXUL(false),
119 mShowAfterLoad(false),
120 mIntrinsicallySized(false),
121 mCenterAfterLoad(false),
122 mIsHiddenWindow(false),
123 mLockedUntilChromeLoad(false),
124 mIgnoreXULSize(false),
125 mIgnoreXULPosition(false),
126 mChromeFlagsFrozen(false),
127 mIgnoreXULSizeMode(false),
128 mDestroying(false),
129 mRegistered(false),
130 mDominantClientSize(false),
131 mChromeFlags(aChromeFlags),
132 mWidgetListenerDelegate(this) {}
134 AppWindow::~AppWindow() {
135 if (mSPTimer) {
136 mSPTimer->Cancel();
137 mSPTimer = nullptr;
139 Destroy();
142 //*****************************************************************************
143 // AppWindow::nsISupports
144 //*****************************************************************************
146 NS_IMPL_ADDREF(AppWindow)
147 NS_IMPL_RELEASE(AppWindow)
149 NS_INTERFACE_MAP_BEGIN(AppWindow)
150 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAppWindow)
151 NS_INTERFACE_MAP_ENTRY(nsIAppWindow)
152 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
153 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
154 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
155 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
156 NS_INTERFACE_MAP_ENTRY_CONCRETE(AppWindow)
157 NS_INTERFACE_MAP_END
159 nsresult AppWindow::Initialize(nsIAppWindow* aParent, nsIAppWindow* aOpener,
160 int32_t aInitialWidth, int32_t aInitialHeight,
161 bool aIsHiddenWindow,
162 widget::InitData& widgetInitData) {
163 nsresult rv;
164 nsCOMPtr<nsIWidget> parentWidget;
166 mIsHiddenWindow = aIsHiddenWindow;
168 DesktopIntPoint initialPos;
169 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aOpener));
170 if (base) {
171 LayoutDeviceIntRect rect = base->GetPositionAndSize();
172 mOpenerScreenRect =
173 DesktopIntRect::Round(rect / base->DevicePixelsPerDesktopPixel());
174 if (!mOpenerScreenRect.IsEmpty()) {
175 initialPos = mOpenerScreenRect.TopLeft();
176 ConstrainToOpenerScreen(&initialPos.x.value, &initialPos.y.value);
180 // XXX: need to get the default window size from prefs...
181 // Doesn't come from prefs... will come from CSS/XUL/RDF
182 DesktopIntRect deskRect(initialPos,
183 DesktopIntSize(aInitialWidth, aInitialHeight));
185 // Create top level window
186 if (gfxPlatform::IsHeadless()) {
187 mWindow = nsIWidget::CreateHeadlessWidget();
188 } else {
189 mWindow = nsIWidget::CreateTopLevelWindow();
191 if (!mWindow) {
192 return NS_ERROR_FAILURE;
195 /* This next bit is troublesome. We carry two different versions of a pointer
196 to our parent window. One is the parent window's widget, which is passed
197 to our own widget. The other is a weak reference we keep here to our
198 parent AppWindow. The former is useful to the widget, and we can't
199 trust its treatment of the parent reference because they're platform-
200 specific. The latter is useful to this class.
201 A better implementation would be one in which the parent keeps strong
202 references to its children and closes them before it allows itself
203 to be closed. This would mimic the behaviour of OSes that support
204 top-level child windows in OSes that do not. Later.
206 nsCOMPtr<nsIBaseWindow> parentAsWin(do_QueryInterface(aParent));
207 if (parentAsWin) {
208 parentAsWin->GetMainWidget(getter_AddRefs(parentWidget));
209 mParentWindow = do_GetWeakReference(aParent);
212 mWindow->SetWidgetListener(&mWidgetListenerDelegate);
213 rv = mWindow->Create((nsIWidget*)parentWidget, // Parent nsIWidget
214 nullptr, // Native parent widget
215 deskRect, // Widget dimensions
216 &widgetInitData); // Widget initialization data
217 NS_ENSURE_SUCCESS(rv, rv);
219 LayoutDeviceIntRect r = mWindow->GetClientBounds();
220 // Match the default background color of content. 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(mDocShell->InitWindow(nullptr, mWindow, r.X(), r.Y(),
242 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(nativeWindow aParentNativeWindow,
496 nsIWidget* parentWidget, int32_t x,
497 int32_t y, int32_t cx, int32_t cy) {
498 // XXX First Check In
499 NS_ASSERTION(false, "Not Yet Implemented");
500 return NS_OK;
503 NS_IMETHODIMP AppWindow::Destroy() {
504 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
506 if (mDocShell) {
507 mDocShell->RemoveProgressListener(this);
510 if (mSPTimer) {
511 mSPTimer->Cancel();
512 SavePersistentAttributes();
513 mSPTimer = nullptr;
516 if (!mWindow) return NS_OK;
518 // Ensure we don't reenter this code
519 if (mDestroying) return NS_OK;
521 mozilla::AutoRestore<bool> guard(mDestroying);
522 mDestroying = true;
524 nsCOMPtr<nsIAppShellService> appShell(
525 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
526 NS_ASSERTION(appShell, "Couldn't get appShell... xpcom shutdown?");
527 if (appShell) {
528 appShell->UnregisterTopLevelWindow(static_cast<nsIAppWindow*>(this));
531 // Remove modality (if any) and hide while destroying. More than
532 // a convenience, the hide prevents user interaction with the partially
533 // destroyed window. This is especially necessary when the eldest window
534 // in a stack of modal windows is destroyed first. It happens.
535 ExitModalLoop(NS_OK);
536 // XXX: Skip unmapping the window on Linux due to GLX hangs on the compositor
537 // thread with NVIDIA driver 310.32. We don't need to worry about user
538 // interactions with destroyed windows on X11 either.
539 #ifndef MOZ_WIDGET_GTK
540 if (mWindow) mWindow->Show(false);
541 #endif
543 RemoveTooltipSupport();
545 mDOMWindow = nullptr;
546 if (mDocShell) {
547 RefPtr<BrowsingContext> bc(mDocShell->GetBrowsingContext());
548 mDocShell->Destroy();
549 bc->Detach();
550 mDocShell = nullptr; // this can cause reentrancy of this function
553 mPrimaryContentShell = nullptr;
555 if (mContentTreeOwner) {
556 mContentTreeOwner->AppWindow(nullptr);
557 NS_RELEASE(mContentTreeOwner);
559 if (mPrimaryContentTreeOwner) {
560 mPrimaryContentTreeOwner->AppWindow(nullptr);
561 NS_RELEASE(mPrimaryContentTreeOwner);
563 if (mChromeTreeOwner) {
564 mChromeTreeOwner->AppWindow(nullptr);
565 NS_RELEASE(mChromeTreeOwner);
567 if (mWindow) {
568 mWindow->SetWidgetListener(nullptr); // nsWebShellWindow hackery
569 mWindow->Destroy();
570 mWindow = nullptr;
573 if (!mIsHiddenWindow && mRegistered) {
574 /* Inform appstartup we've destroyed this window and it could
575 quit now if it wanted. This must happen at least after mDocShell
576 is destroyed, because onunload handlers fire then, and those being
577 script, anything could happen. A new window could open, even.
578 See bug 130719. */
579 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
580 NS_ASSERTION(obssvc, "Couldn't get observer service?");
582 if (obssvc)
583 obssvc->NotifyObservers(nullptr, "xul-window-destroyed", nullptr);
586 return NS_OK;
589 NS_IMETHODIMP AppWindow::GetDevicePixelsPerDesktopPixel(double* aScale) {
590 *aScale = mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0;
591 return NS_OK;
594 double AppWindow::GetWidgetCSSToDeviceScale() {
595 return mWindow ? mWindow->GetDefaultScale().scale : 1.0;
598 NS_IMETHODIMP AppWindow::SetPositionDesktopPix(int32_t aX, int32_t aY) {
599 return MoveResize(Some(DesktopIntPoint(aX, aY)), Nothing(), false);
602 // The parameters here are device pixels; do the best we can to convert to
603 // desktop px, using the window's current scale factor (if available).
604 NS_IMETHODIMP AppWindow::SetPosition(int32_t aX, int32_t aY) {
605 // Don't reset the window's size mode here - platforms that don't want to move
606 // maximized windows should reset it in their respective Move implementation.
607 return MoveResize(Some(LayoutDeviceIntPoint(aX, aY)), Nothing(), false);
610 NS_IMETHODIMP AppWindow::GetPosition(int32_t* aX, int32_t* aY) {
611 return GetPositionAndSize(aX, aY, nullptr, nullptr);
614 NS_IMETHODIMP AppWindow::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) {
615 /* any attempt to set the window's size or position overrides the window's
616 zoom state. this is important when these two states are competing while
617 the window is being opened. but it should probably just always be so. */
618 return MoveResize(Nothing(), Some(LayoutDeviceIntSize(aCX, aCY)), aRepaint);
621 NS_IMETHODIMP AppWindow::GetSize(int32_t* aCX, int32_t* aCY) {
622 return GetPositionAndSize(nullptr, nullptr, aCX, aCY);
625 NS_IMETHODIMP AppWindow::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
626 int32_t aCY, uint32_t aFlags) {
627 /* any attempt to set the window's size or position overrides the window's
628 zoom state. this is important when these two states are competing while
629 the window is being opened. but it should probably just always be so. */
630 return MoveResize(Some(LayoutDeviceIntPoint(aX, aY)),
631 Some(LayoutDeviceIntSize(aCX, aCY)),
632 !!(aFlags & nsIBaseWindow::eRepaint));
635 NS_IMETHODIMP AppWindow::GetPositionAndSize(int32_t* x, int32_t* y, int32_t* cx,
636 int32_t* cy) {
637 if (!mWindow) return NS_ERROR_FAILURE;
639 LayoutDeviceIntRect rect = mWindow->GetScreenBounds();
641 if (x) *x = rect.X();
642 if (y) *y = rect.Y();
643 if (cx) *cx = rect.Width();
644 if (cy) *cy = rect.Height();
646 return NS_OK;
649 NS_IMETHODIMP
650 AppWindow::SetDimensions(DimensionRequest&& aRequest) {
651 if (aRequest.mDimensionKind == DimensionKind::Inner) {
652 // For the chrome the inner size is the root shell size, and for the
653 // content it's the primary content size. We lack an indicator here that
654 // would allow us to distinguish between the two.
655 return NS_ERROR_NOT_IMPLEMENTED;
658 MOZ_TRY(aRequest.SupplementFrom(this));
659 return aRequest.ApplyOuterTo(this);
662 NS_IMETHODIMP
663 AppWindow::GetDimensions(DimensionKind aDimensionKind, int32_t* aX, int32_t* aY,
664 int32_t* aCX, int32_t* aCY) {
665 if (aDimensionKind == DimensionKind::Inner) {
666 // For the chrome the inner size is the root shell size, and for the
667 // content it's the primary content size. We lack an indicator here that
668 // would allow us to distinguish between the two.
669 return NS_ERROR_NOT_IMPLEMENTED;
671 return GetPositionAndSize(aX, aY, aCX, aCY);
674 nsresult AppWindow::MoveResize(const Maybe<LayoutDeviceIntPoint>& aPosition,
675 const Maybe<LayoutDeviceIntSize>& aSize,
676 bool aRepaint) {
677 NS_ENSURE_STATE(mWindow);
678 DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
679 return MoveResize(aPosition ? Some(*aPosition / scale) : Nothing(),
680 aSize ? Some(*aSize / scale) : Nothing(), aRepaint);
683 nsresult AppWindow::MoveResize(const Maybe<DesktopPoint>& aPosition,
684 const Maybe<DesktopSize>& aSize, bool aRepaint) {
685 NS_ENSURE_STATE(mWindow);
686 PersistentAttributes dirtyAttributes;
688 if (!aPosition && !aSize) {
689 MOZ_ASSERT_UNREACHABLE("Doing nothing?");
690 return NS_ERROR_UNEXPECTED;
693 if (aSize) {
694 mWindow->SetSizeMode(nsSizeMode_Normal);
695 mIntrinsicallySized = false;
696 mDominantClientSize = false;
699 if (aPosition && aSize) {
700 mWindow->Resize(aPosition->x, aPosition->y, aSize->width, aSize->height,
701 aRepaint);
702 dirtyAttributes = {PersistentAttribute::Size,
703 PersistentAttribute::Position};
704 } else if (aSize) {
705 mWindow->Resize(aSize->width, aSize->height, aRepaint);
706 dirtyAttributes = {PersistentAttribute::Size};
707 } else if (aPosition) {
708 mWindow->Move(aPosition->x, aPosition->y);
709 dirtyAttributes = {PersistentAttribute::Position};
712 if (mSizingShellFromXUL) {
713 // If we're invoked for sizing from XUL, we want to neither ignore anything
714 // nor persist anything, since it's already the value in XUL.
715 return NS_OK;
717 if (!mChromeLoaded) {
718 // If we're called before the chrome is loaded someone obviously wants this
719 // window at this size & in the normal size mode (since it is the only mode
720 // in which setting dimensions makes sense). We don't persist this one-time
721 // position/size.
722 if (aPosition) {
723 mIgnoreXULPosition = true;
725 if (aSize) {
726 mIgnoreXULSize = true;
727 mIgnoreXULSizeMode = true;
729 return NS_OK;
732 PersistentAttributesDirty(dirtyAttributes, Sync);
733 return NS_OK;
736 NS_IMETHODIMP AppWindow::Center(nsIAppWindow* aRelative, bool aScreen,
737 bool aAlert) {
738 DesktopIntRect rect;
739 bool screenCoordinates = false, windowCoordinates = false;
740 nsresult result;
742 if (!mChromeLoaded) {
743 // note we lose the parameters. at time of writing, this isn't a problem.
744 mCenterAfterLoad = true;
745 return NS_OK;
748 if (!aScreen && !aRelative) return NS_ERROR_INVALID_ARG;
750 nsCOMPtr<nsIScreenManager> screenmgr =
751 do_GetService("@mozilla.org/gfx/screenmanager;1", &result);
752 if (NS_FAILED(result)) {
753 return result;
756 nsCOMPtr<nsIScreen> screen;
758 if (aRelative) {
759 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aRelative));
760 if (base) {
761 rect = RoundedToInt(base->GetPositionAndSize() /
762 base->DevicePixelsPerDesktopPixel());
763 // if centering on screen, convert that to the corresponding screen
764 if (aScreen) {
765 screen = screenmgr->ScreenForRect(rect);
766 } else {
767 windowCoordinates = true;
771 if (!aRelative) {
772 if (!mOpenerScreenRect.IsEmpty()) {
773 screen = screenmgr->ScreenForRect(mOpenerScreenRect);
774 } else {
775 screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
779 if (aScreen && screen) {
780 rect = screen->GetAvailRectDisplayPix();
781 screenCoordinates = true;
784 if (!screenCoordinates && !windowCoordinates) {
785 return NS_ERROR_FAILURE;
788 NS_ASSERTION(mWindow, "what, no window?");
789 const LayoutDeviceIntSize ourDevSize = GetSize();
790 const DesktopIntSize ourSize =
791 RoundedToInt(ourDevSize / DevicePixelsPerDesktopPixel());
792 auto newPos =
793 rect.TopLeft() +
794 DesktopIntPoint((rect.width - ourSize.width) / 2,
795 (rect.height - ourSize.height) / (aAlert ? 3 : 2));
796 if (windowCoordinates) {
797 mWindow->ConstrainPosition(newPos);
800 SetPositionDesktopPix(newPos.x, newPos.y);
802 // If moving the window caused it to change size, re-do the centering.
803 if (GetSize() != ourDevSize) {
804 return Center(aRelative, aScreen, aAlert);
806 return NS_OK;
809 NS_IMETHODIMP AppWindow::Repaint(bool aForce) {
810 // XXX First Check In
811 NS_ASSERTION(false, "Not Yet Implemented");
812 return NS_OK;
815 NS_IMETHODIMP AppWindow::GetParentWidget(nsIWidget** aParentWidget) {
816 NS_ENSURE_ARG_POINTER(aParentWidget);
817 NS_ENSURE_STATE(mWindow);
819 NS_IF_ADDREF(*aParentWidget = mWindow->GetParent());
820 return NS_OK;
823 NS_IMETHODIMP AppWindow::SetParentWidget(nsIWidget* aParentWidget) {
824 // XXX First Check In
825 NS_ASSERTION(false, "Not Yet Implemented");
826 return NS_OK;
829 NS_IMETHODIMP AppWindow::GetParentNativeWindow(
830 nativeWindow* aParentNativeWindow) {
831 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
833 nsCOMPtr<nsIWidget> parentWidget;
834 NS_ENSURE_SUCCESS(GetParentWidget(getter_AddRefs(parentWidget)),
835 NS_ERROR_FAILURE);
837 if (parentWidget) {
838 *aParentNativeWindow = parentWidget->GetNativeData(NS_NATIVE_WIDGET);
841 return NS_OK;
844 NS_IMETHODIMP AppWindow::SetParentNativeWindow(
845 nativeWindow aParentNativeWindow) {
846 // XXX First Check In
847 NS_ASSERTION(false, "Not Yet Implemented");
848 return NS_OK;
851 NS_IMETHODIMP AppWindow::GetNativeHandle(nsAString& aNativeHandle) {
852 nsCOMPtr<nsIWidget> mainWidget;
853 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(mainWidget)),
854 NS_ERROR_FAILURE);
856 if (mainWidget) {
857 nativeWindow nativeWindowPtr = mainWidget->GetNativeData(NS_NATIVE_WINDOW);
858 /* the nativeWindow pointer is converted to and exposed as a string. This
859 is a more reliable way not to lose information (as opposed to JS
860 |Number| for instance) */
861 aNativeHandle =
862 NS_ConvertASCIItoUTF16(nsPrintfCString("0x%p", nativeWindowPtr));
865 return NS_OK;
868 NS_IMETHODIMP AppWindow::GetVisibility(bool* aVisibility) {
869 NS_ENSURE_ARG_POINTER(aVisibility);
871 // Always claim to be visible for now. See bug
872 // https://bugzilla.mozilla.org/show_bug.cgi?id=306245.
874 *aVisibility = true;
876 return NS_OK;
879 NS_IMETHODIMP AppWindow::SetVisibility(bool aVisibility) {
880 if (!mChromeLoaded) {
881 mShowAfterLoad = aVisibility;
882 return NS_OK;
885 if (mDebuting) {
886 return NS_OK;
889 NS_ENSURE_STATE(mDocShell);
891 mDebuting = true; // (Show / Focus is recursive)
893 // XXXTAB Do we really need to show docshell and the window? Isn't
894 // the window good enough?
895 mDocShell->SetVisibility(aVisibility);
896 // Store locally so it doesn't die on us. 'Show' can result in the window
897 // being closed with AppWindow::Destroy being called. That would set
898 // mWindow to null and posibly destroy the nsIWidget while its Show method
899 // is on the stack. We need to keep it alive until Show finishes.
900 nsCOMPtr<nsIWidget> window = mWindow;
901 window->Show(aVisibility);
903 nsCOMPtr<nsIWindowMediator> windowMediator(
904 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
905 if (windowMediator)
906 windowMediator->UpdateWindowTimeStamp(static_cast<nsIAppWindow*>(this));
908 // notify observers so that we can hide the splash screen if possible
909 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
910 NS_ASSERTION(obssvc, "Couldn't get observer service.");
911 if (obssvc) {
912 obssvc->NotifyObservers(static_cast<nsIAppWindow*>(this),
913 "xul-window-visible", nullptr);
916 mDebuting = false;
917 return NS_OK;
920 NS_IMETHODIMP AppWindow::GetEnabled(bool* aEnabled) {
921 NS_ENSURE_ARG_POINTER(aEnabled);
923 if (mWindow) {
924 *aEnabled = mWindow->IsEnabled();
925 return NS_OK;
928 *aEnabled = true; // better guess than most
929 return NS_ERROR_FAILURE;
932 NS_IMETHODIMP AppWindow::SetEnabled(bool aEnable) {
933 if (mWindow) {
934 mWindow->Enable(aEnable);
935 return NS_OK;
937 return NS_ERROR_FAILURE;
940 NS_IMETHODIMP AppWindow::GetMainWidget(nsIWidget** aMainWidget) {
941 NS_ENSURE_ARG_POINTER(aMainWidget);
942 NS_IF_ADDREF(*aMainWidget = mWindow);
943 return NS_OK;
946 NS_IMETHODIMP AppWindow::GetTitle(nsAString& aTitle) {
947 aTitle = mTitle;
948 return NS_OK;
951 NS_IMETHODIMP AppWindow::SetTitle(const nsAString& aTitle) {
952 NS_ENSURE_STATE(mWindow);
953 mTitle.Assign(aTitle);
954 mTitle.StripCRLF();
955 NS_ENSURE_SUCCESS(mWindow->SetTitle(mTitle), NS_ERROR_FAILURE);
956 return NS_OK;
959 //*****************************************************************************
960 // AppWindow: Helpers
961 //*****************************************************************************
963 NS_IMETHODIMP AppWindow::EnsureChromeTreeOwner() {
964 if (mChromeTreeOwner) return NS_OK;
966 mChromeTreeOwner = new nsChromeTreeOwner();
967 NS_ADDREF(mChromeTreeOwner);
968 mChromeTreeOwner->AppWindow(this);
970 return NS_OK;
973 NS_IMETHODIMP AppWindow::EnsureContentTreeOwner() {
974 if (mContentTreeOwner) return NS_OK;
976 mContentTreeOwner = new nsContentTreeOwner(false);
977 NS_ADDREF(mContentTreeOwner);
978 mContentTreeOwner->AppWindow(this);
980 return NS_OK;
983 NS_IMETHODIMP AppWindow::EnsurePrimaryContentTreeOwner() {
984 if (mPrimaryContentTreeOwner) return NS_OK;
986 mPrimaryContentTreeOwner = new nsContentTreeOwner(true);
987 NS_ADDREF(mPrimaryContentTreeOwner);
988 mPrimaryContentTreeOwner->AppWindow(this);
990 return NS_OK;
993 NS_IMETHODIMP AppWindow::EnsurePrompter() {
994 if (mPrompter) return NS_OK;
996 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
997 nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow));
998 if (NS_SUCCEEDED(rv)) {
999 nsCOMPtr<nsIWindowWatcher> wwatch =
1000 do_GetService(NS_WINDOWWATCHER_CONTRACTID);
1001 if (wwatch) wwatch->GetNewPrompter(ourWindow, getter_AddRefs(mPrompter));
1003 return mPrompter ? NS_OK : NS_ERROR_FAILURE;
1006 NS_IMETHODIMP AppWindow::EnsureAuthPrompter() {
1007 if (mAuthPrompter) return NS_OK;
1009 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1010 nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow));
1011 if (NS_SUCCEEDED(rv)) {
1012 nsCOMPtr<nsIWindowWatcher> wwatch(
1013 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
1014 if (wwatch)
1015 wwatch->GetNewAuthPrompter(ourWindow, getter_AddRefs(mAuthPrompter));
1017 return mAuthPrompter ? NS_OK : NS_ERROR_FAILURE;
1020 NS_IMETHODIMP AppWindow::GetAvailScreenSize(int32_t* aAvailWidth,
1021 int32_t* aAvailHeight) {
1022 nsCOMPtr<mozIDOMWindowProxy> domWindow;
1023 GetWindowDOMWindow(getter_AddRefs(domWindow));
1024 NS_ENSURE_STATE(domWindow);
1026 auto* window = nsGlobalWindowOuter::Cast(domWindow);
1028 RefPtr<nsScreen> screen = window->GetScreen();
1029 NS_ENSURE_STATE(screen);
1031 *aAvailWidth = screen->AvailWidth();
1032 *aAvailHeight = screen->AvailHeight();
1033 return NS_OK;
1036 // Rounds window size to 1000x1000, or, if there isn't enough available
1037 // screen space, to a multiple of 200x100.
1038 NS_IMETHODIMP AppWindow::ForceRoundedDimensions() {
1039 if (mIsHiddenWindow) {
1040 return NS_OK;
1043 CSSToLayoutDeviceScale scale = UnscaledDevicePixelsPerCSSPixel();
1045 CSSIntSize availSizeCSS;
1046 GetAvailScreenSize(&availSizeCSS.width, &availSizeCSS.height);
1048 // To get correct chrome size, we have to resize the window to a proper
1049 // size first. So, here, we size it to its available size.
1050 SetSpecifiedSize(availSizeCSS.width, availSizeCSS.height);
1052 // Get the current window size for calculating chrome UI size.
1053 CSSIntSize windowSizeCSS = RoundedToInt(GetSize() / scale);
1055 // Get the content size for calculating chrome UI size.
1056 LayoutDeviceIntSize contentSizeDev;
1057 GetPrimaryContentSize(&contentSizeDev.width, &contentSizeDev.height);
1058 CSSIntSize contentSizeCSS = RoundedToInt(contentSizeDev / scale);
1060 // Calculate the chrome UI size.
1061 CSSIntSize chromeSizeCSS = windowSizeCSS - contentSizeCSS;
1063 CSSIntSize targetSizeCSS;
1064 // Here, we use the available screen dimensions as the input dimensions to
1065 // force the window to be rounded as the maximum available content size.
1066 nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
1067 chromeSizeCSS.width, chromeSizeCSS.height, availSizeCSS.width,
1068 availSizeCSS.height, availSizeCSS.width, availSizeCSS.height,
1069 false, // aSetOuterWidth
1070 false, // aSetOuterHeight
1071 &targetSizeCSS.width, &targetSizeCSS.height);
1073 LayoutDeviceIntSize targetSizeDev = RoundedToInt(targetSizeCSS * scale);
1075 SetPrimaryContentSize(targetSizeDev.width, targetSizeDev.height);
1077 return NS_OK;
1080 void AppWindow::OnChromeLoaded() {
1081 nsresult rv = EnsureContentTreeOwner();
1083 if (NS_SUCCEEDED(rv)) {
1084 mChromeLoaded = true;
1085 ApplyChromeFlags();
1086 SyncAttributesToWidget();
1087 if (mWindow) {
1088 SizeShell();
1089 if (mShowAfterLoad) {
1090 SetVisibility(true);
1092 AddTooltipSupport();
1094 // At this point the window may have been closed already during Show() or
1095 // SyncAttributesToWidget(), so AppWindow::Destroy may already have been
1096 // called. Take care!
1098 mPersistentAttributesMask += AllPersistentAttributes();
1101 bool AppWindow::NeedsTooltipListener() {
1102 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1103 if (!docShellElement || docShellElement->IsXULElement()) {
1104 // Tooltips in XUL are handled by each element.
1105 return false;
1107 // All other non-XUL document types need a tooltip listener.
1108 return true;
1111 void AppWindow::AddTooltipSupport() {
1112 if (!NeedsTooltipListener()) {
1113 return;
1115 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
1116 if (!listener) {
1117 return;
1120 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1121 MOZ_ASSERT(docShellElement);
1122 listener->AddTooltipSupport(docShellElement);
1125 void AppWindow::RemoveTooltipSupport() {
1126 if (!NeedsTooltipListener()) {
1127 return;
1129 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
1130 if (!listener) {
1131 return;
1134 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1135 MOZ_ASSERT(docShellElement);
1136 listener->RemoveTooltipSupport(docShellElement);
1139 static Maybe<int32_t> ReadIntAttribute(const Element& aElement,
1140 nsAtom* aPrimary,
1141 nsAtom* aSecondary = nullptr) {
1142 nsAutoString attrString;
1143 if (!aElement.GetAttr(aPrimary, attrString)) {
1144 if (aSecondary) {
1145 return ReadIntAttribute(aElement, aSecondary);
1147 return Nothing();
1150 nsresult res = NS_OK;
1151 int32_t ret = attrString.ToInteger(&res);
1152 return NS_SUCCEEDED(res) ? Some(ret) : Nothing();
1155 // If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes
1156 // to fit to the screen when staggering windows; if they're negative,
1157 // we use the window's current size instead.
1158 bool AppWindow::LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight) {
1159 bool gotPosition = false;
1161 // if we're the hidden window, don't try to validate our size/position. We're
1162 // special.
1163 if (mIsHiddenWindow) {
1164 return false;
1167 RefPtr<dom::Element> root = GetWindowDOMElement();
1168 NS_ENSURE_TRUE(root, false);
1170 const LayoutDeviceIntRect devRect = GetPositionAndSize();
1172 // Convert to global display pixels for consistent window management across
1173 // screens with diverse resolutions
1174 const DesktopIntPoint curPoint =
1175 RoundedToInt(devRect.TopLeft() / DevicePixelsPerDesktopPixel());
1177 // For size, use specified value if > 0, else current value
1178 CSSIntSize cssSize(aSpecWidth, aSpecHeight);
1180 CSSIntSize currentSize =
1181 RoundedToInt(devRect.Size() / UnscaledDevicePixelsPerCSSPixel());
1182 if (aSpecHeight <= 0) {
1183 cssSize.height = currentSize.height;
1185 if (aSpecWidth <= 0) {
1186 cssSize.width = currentSize.width;
1190 // Obtain the position information from the <xul:window> element.
1191 DesktopIntPoint specPoint = curPoint;
1193 // Also read lowercase screenx/y because the front-end sometimes sets these
1194 // via setAttribute on HTML documents like about:blank, and stuff gets
1195 // lowercased.
1197 // TODO(emilio): We should probably rename screenX/Y to screen-x/y to
1198 // prevent this impedance mismatch.
1199 if (auto attr =
1200 ReadIntAttribute(*root, nsGkAtoms::screenX, nsGkAtoms::screenx)) {
1201 specPoint.x = *attr;
1202 gotPosition = true;
1205 if (auto attr =
1206 ReadIntAttribute(*root, nsGkAtoms::screenY, nsGkAtoms::screeny)) {
1207 specPoint.y = *attr;
1208 gotPosition = true;
1211 if (gotPosition) {
1212 // Our position will be relative to our parent, if any
1213 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
1214 if (parent) {
1215 const DesktopIntPoint parentPos = RoundedToInt(
1216 parent->GetPosition() / parent->DevicePixelsPerDesktopPixel());
1217 specPoint += parentPos;
1218 } else {
1219 StaggerPosition(specPoint.x.value, specPoint.y.value, cssSize.width,
1220 cssSize.height);
1223 mWindow->ConstrainPosition(specPoint);
1224 if (specPoint != curPoint) {
1225 SetPositionDesktopPix(specPoint.x, specPoint.y);
1228 return gotPosition;
1231 static Maybe<int32_t> ReadSize(const Element& aElement, nsAtom* aAttr,
1232 nsAtom* aMinAttr, nsAtom* aMaxAttr) {
1233 Maybe<int32_t> attr = ReadIntAttribute(aElement, aAttr);
1234 if (!attr) {
1235 return Nothing();
1238 int32_t min =
1239 std::max(100, ReadIntAttribute(aElement, aMinAttr).valueOr(100));
1240 int32_t max = ReadIntAttribute(aElement, aMaxAttr)
1241 .valueOr(std::numeric_limits<int32_t>::max());
1243 return Some(std::min(max, std::max(*attr, min)));
1246 bool AppWindow::LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight) {
1247 bool gotSize = false;
1249 // if we're the hidden window, don't try to validate our size/position. We're
1250 // special.
1251 if (mIsHiddenWindow) {
1252 return false;
1255 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1256 NS_ENSURE_TRUE(windowElement, false);
1258 // Obtain the sizing information from the <xul:window> element.
1259 aSpecWidth = 100;
1260 aSpecHeight = 100;
1262 if (auto width = ReadSize(*windowElement, nsGkAtoms::width,
1263 nsGkAtoms::minwidth, nsGkAtoms::maxwidth)) {
1264 aSpecWidth = *width;
1265 gotSize = true;
1268 if (auto height = ReadSize(*windowElement, nsGkAtoms::height,
1269 nsGkAtoms::minheight, nsGkAtoms::maxheight)) {
1270 aSpecHeight = *height;
1271 gotSize = true;
1274 return gotSize;
1277 void AppWindow::SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight) {
1278 // These are in CSS pixels of the main window.
1279 // TODO(emilio): In my testing we usually have a pres context around, can we
1280 // just use it? That'd simplify the coordinate calculations.
1282 int32_t screenWidth;
1283 int32_t screenHeight;
1285 if (NS_SUCCEEDED(GetAvailScreenSize(&screenWidth, &screenHeight))) {
1286 if (aSpecWidth > screenWidth) {
1287 aSpecWidth = screenWidth;
1289 if (aSpecHeight > screenHeight) {
1290 aSpecHeight = screenHeight;
1295 NS_ASSERTION(mWindow, "we expected to have a window already");
1297 mIntrinsicallySized = false;
1299 // Convert specified values to device pixels, and resize
1300 auto newSize = RoundedToInt(CSSIntSize(aSpecWidth, aSpecHeight) *
1301 UnscaledDevicePixelsPerCSSPixel());
1303 // Note: Because of the asynchronous resizing on Linux we have to call
1304 // SetSize even when the size doesn't appear to change. A previous call that
1305 // has yet to complete can still change the size. We want the latest call to
1306 // define the final size.
1307 SetSize(newSize.width, newSize.height, false);
1310 /* Miscellaneous persistent attributes are attributes named in the
1311 |persist| attribute, other than size and position. Those are special
1312 because it's important to load those before one of the misc
1313 attributes (sizemode) and they require extra processing. */
1314 bool AppWindow::UpdateWindowStateFromMiscXULAttributes() {
1315 /* There are no misc attributes of interest to the hidden window.
1316 It's especially important not to try to validate that window's
1317 size or position, because some platforms (Mac OS X) need to
1318 make it visible and offscreen. */
1319 if (mIsHiddenWindow) {
1320 return false;
1323 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1324 NS_ENSURE_TRUE(windowElement, false);
1326 nsAutoString stateString;
1327 nsSizeMode sizeMode = nsSizeMode_Normal;
1329 // If we are told to ignore the size mode attribute, force
1330 // normal sizemode.
1331 if (mIgnoreXULSizeMode) {
1332 windowElement->SetAttr(nsGkAtoms::sizemode, SIZEMODE_NORMAL,
1333 IgnoreErrors());
1334 } else {
1335 // Otherwise, read sizemode from DOM and, if the window is resizable,
1336 // set it later.
1337 windowElement->GetAttr(nsGkAtoms::sizemode, stateString);
1338 if ((stateString.Equals(SIZEMODE_MAXIMIZED) ||
1339 stateString.Equals(SIZEMODE_FULLSCREEN))) {
1340 /* Honor request to maximize only if the window is sizable.
1341 An unsizable, unmaximizable, yet maximized window confuses
1342 Windows OS and is something of a travesty, anyway. */
1343 if (mChromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
1344 mIntrinsicallySized = false;
1346 sizeMode = stateString.Equals(SIZEMODE_MAXIMIZED)
1347 ? nsSizeMode_Maximized
1348 : nsSizeMode_Fullscreen;
1353 if (sizeMode == nsSizeMode_Fullscreen) {
1354 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1355 GetWindowDOMWindow(getter_AddRefs(ourWindow));
1356 auto* piWindow = nsPIDOMWindowOuter::From(ourWindow);
1357 piWindow->SetFullScreen(true);
1358 } else {
1359 // For maximized windows, ignore the XUL size and position attributes,
1360 // as setting them would set the window back to normal sizemode.
1361 if (sizeMode == nsSizeMode_Maximized) {
1362 mIgnoreXULSize = true;
1363 mIgnoreXULPosition = true;
1365 mWindow->SetSizeMode(sizeMode);
1367 return true;
1370 /* Stagger windows of the same type so they don't appear on top of each other.
1371 This code does have a scary double loop -- it'll keep passing through
1372 the entire list of open windows until it finds a non-collision. Doesn't
1373 seem to be a problem, but it deserves watching.
1374 The aRequested{X,Y} parameters here are in desktop pixels;
1375 the aSpec{Width,Height} parameters are CSS pixel dimensions.
1377 void AppWindow::StaggerPosition(int32_t& aRequestedX, int32_t& aRequestedY,
1378 int32_t aSpecWidth, int32_t aSpecHeight) {
1379 // These "constants" will be converted from CSS to desktop pixels
1380 // for the appropriate screen, assuming we find a screen to use...
1381 // hence they're not actually declared const here.
1382 int32_t kOffset = 22;
1383 uint32_t kSlop = 4;
1385 bool keepTrying;
1386 int bouncedX = 0, // bounced off vertical edge of screen
1387 bouncedY = 0; // bounced off horizontal edge
1389 // look for any other windows of this type
1390 nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
1391 if (!wm) return;
1393 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1394 if (!windowElement) return;
1396 nsCOMPtr<nsIAppWindow> ourAppWindow(this);
1398 nsAutoString windowType;
1399 windowElement->GetAttr(nsGkAtoms::windowtype, windowType);
1401 DesktopIntRect screenRect;
1402 bool gotScreen = false;
1404 { // fetch screen coordinates
1405 nsCOMPtr<nsIScreenManager> screenMgr(
1406 do_GetService("@mozilla.org/gfx/screenmanager;1"));
1407 if (screenMgr) {
1408 nsCOMPtr<nsIScreen> ourScreen;
1409 // The coordinates here are already display pixels
1410 // XXX aSpecWidth and aSpecHeight are CSS pixels!
1411 screenMgr->ScreenForRect(aRequestedX, aRequestedY, aSpecWidth,
1412 aSpecHeight, getter_AddRefs(ourScreen));
1413 if (ourScreen) {
1414 screenRect = ourScreen->GetAvailRectDisplayPix();
1416 // Get the screen's scaling factors and convert staggering constants
1417 // from CSS px to desktop pixel units
1418 auto scale = ourScreen->GetCSSToDesktopScale();
1419 kOffset = (CSSCoord(kOffset) * scale).Rounded();
1420 kSlop = (CSSCoord(kSlop) * scale).Rounded();
1421 // Convert dimensions from CSS to desktop pixels
1422 aSpecWidth = (CSSCoord(aSpecWidth) * scale).Rounded();
1423 aSpecHeight = (CSSCoord(aSpecHeight) * scale).Rounded();
1424 gotScreen = true;
1429 // One full pass through all windows of this type, repeat until no collisions.
1430 do {
1431 keepTrying = false;
1432 nsCOMPtr<nsISimpleEnumerator> windowList;
1433 wm->GetAppWindowEnumerator(windowType.get(), getter_AddRefs(windowList));
1435 if (!windowList) break;
1437 // One full pass through all windows of this type, offset and stop on
1438 // collision.
1439 do {
1440 bool more;
1441 windowList->HasMoreElements(&more);
1442 if (!more) break;
1444 nsCOMPtr<nsISupports> supportsWindow;
1445 windowList->GetNext(getter_AddRefs(supportsWindow));
1447 nsCOMPtr<nsIAppWindow> listAppWindow(do_QueryInterface(supportsWindow));
1448 if (listAppWindow != ourAppWindow) {
1449 int32_t listX, listY;
1450 nsCOMPtr<nsIBaseWindow> listBaseWindow(
1451 do_QueryInterface(supportsWindow));
1452 listBaseWindow->GetPosition(&listX, &listY);
1453 double scale;
1454 if (NS_SUCCEEDED(
1455 listBaseWindow->GetDevicePixelsPerDesktopPixel(&scale))) {
1456 listX = NSToIntRound(listX / scale);
1457 listY = NSToIntRound(listY / scale);
1460 if (Abs(listX - aRequestedX) <= kSlop &&
1461 Abs(listY - aRequestedY) <= kSlop) {
1462 // collision! offset and start over
1463 if (bouncedX & 0x1)
1464 aRequestedX -= kOffset;
1465 else
1466 aRequestedX += kOffset;
1467 aRequestedY += kOffset;
1469 if (gotScreen) {
1470 // if we're moving to the right and we need to bounce...
1471 if (!(bouncedX & 0x1) &&
1472 ((aRequestedX + aSpecWidth) > screenRect.XMost())) {
1473 aRequestedX = screenRect.XMost() - aSpecWidth;
1474 ++bouncedX;
1477 // if we're moving to the left and we need to bounce...
1478 if ((bouncedX & 0x1) && aRequestedX < screenRect.X()) {
1479 aRequestedX = screenRect.X();
1480 ++bouncedX;
1483 // if we hit the bottom then bounce to the top
1484 if (aRequestedY + aSpecHeight > screenRect.YMost()) {
1485 aRequestedY = screenRect.Y();
1486 ++bouncedY;
1490 /* loop around again,
1491 but it's time to give up once we've covered the screen.
1492 there's a potential infinite loop with lots of windows. */
1493 keepTrying = bouncedX < 2 || bouncedY == 0;
1494 break;
1497 } while (true);
1498 } while (keepTrying);
1501 void AppWindow::SyncAttributesToWidget() {
1502 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1503 if (!windowElement) return;
1505 MOZ_DIAGNOSTIC_ASSERT(mWindow, "No widget on SyncAttributesToWidget?");
1507 nsAutoString attr;
1509 // Some attributes can change the client size (e.g. chromemargin on Windows
1510 // and MacOS). But we might want to keep it.
1511 const LayoutDeviceIntSize oldClientSize = mWindow->GetClientSize();
1512 // We have to check now whether we want to restore the client size, as any
1513 // change in size will reset its state.
1514 bool maintainClientSize = mDominantClientSize;
1516 // "hidechrome" attribute
1517 if (windowElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidechrome,
1518 nsGkAtoms::_true, eCaseMatters)) {
1519 mWindow->HideWindowChrome(true);
1522 NS_ENSURE_TRUE_VOID(mWindow);
1524 // "chromemargin" attribute
1525 nsIntMargin margins;
1526 windowElement->GetAttribute(u"chromemargin"_ns, attr);
1527 if (nsContentUtils::ParseIntMarginValue(attr, margins)) {
1528 mWindow->SetNonClientMargins(
1529 LayoutDeviceIntMargin::FromUnknownMargin(margins));
1532 NS_ENSURE_TRUE_VOID(mWindow);
1534 // "windowtype", "windowclass", "windowname" attributes
1535 nsAutoString windowClassAttr, windowNameAttr;
1536 windowElement->GetAttr(nsGkAtoms::windowtype, attr);
1537 windowElement->GetAttribute(u"windowclass"_ns, windowClassAttr);
1538 windowElement->GetAttribute(u"windowname"_ns, windowNameAttr);
1539 mWindow->SetWindowClass(attr, windowClassAttr, windowNameAttr);
1541 NS_ENSURE_TRUE_VOID(mWindow);
1543 // Only change blank window status once we're loaded, so that a
1544 // partially-loaded browser window doesn't start painting early.
1545 if (mChromeLoaded) {
1546 mWindow->SetIsEarlyBlankWindow(attr.EqualsLiteral("navigator:blank"));
1547 NS_ENSURE_TRUE_VOID(mWindow);
1550 // "icon" attribute
1551 windowElement->GetAttribute(u"icon"_ns, attr);
1552 if (!attr.IsEmpty()) {
1553 mWindow->SetIcon(attr);
1554 NS_ENSURE_TRUE_VOID(mWindow);
1557 // "drawtitle" attribute
1558 windowElement->GetAttribute(u"drawtitle"_ns, attr);
1559 mWindow->SetDrawsTitle(attr.LowerCaseEqualsLiteral("true"));
1560 NS_ENSURE_TRUE_VOID(mWindow);
1562 // "toggletoolbar" attribute
1563 windowElement->GetAttribute(u"toggletoolbar"_ns, attr);
1564 mWindow->SetShowsToolbarButton(attr.LowerCaseEqualsLiteral("true"));
1565 NS_ENSURE_TRUE_VOID(mWindow);
1567 // "macnativefullscreen" attribute
1568 windowElement->GetAttribute(u"macnativefullscreen"_ns, attr);
1569 mWindow->SetSupportsNativeFullscreen(attr.LowerCaseEqualsLiteral("true"));
1570 NS_ENSURE_TRUE_VOID(mWindow);
1572 // "macanimationtype" attribute
1573 windowElement->GetAttribute(u"macanimationtype"_ns, attr);
1574 if (attr.EqualsLiteral("document")) {
1575 mWindow->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation);
1578 // Check if the client size did change and if we want to restore it.
1579 if (maintainClientSize && mWindow->SizeMode() == nsSizeMode_Normal &&
1580 oldClientSize != mWindow->GetClientSize()) {
1581 mWindow->ResizeClient(oldClientSize / mWindow->GetDesktopToDeviceScale(),
1582 true);
1583 mDominantClientSize = true;
1587 enum class ConversionDirection {
1588 InnerToOuter,
1589 OuterToInner,
1592 static void ConvertWindowSize(nsIAppWindow* aWin, const nsAtom* aAttr,
1593 ConversionDirection aDirection,
1594 nsAString& aInOutString) {
1595 MOZ_ASSERT(aWin);
1596 MOZ_ASSERT(aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height);
1598 nsresult rv;
1599 int32_t size = aInOutString.ToInteger(&rv);
1600 if (NS_FAILED(rv)) {
1601 return;
1604 int32_t sizeDiff = aAttr == nsGkAtoms::width
1605 ? aWin->GetOuterToInnerWidthDifferenceInCSSPixels()
1606 : aWin->GetOuterToInnerHeightDifferenceInCSSPixels();
1608 if (!sizeDiff) {
1609 return;
1612 int32_t multiplier = aDirection == ConversionDirection::InnerToOuter ? 1 : -1;
1614 CopyASCIItoUTF16(nsPrintfCString("%d", size + multiplier * sizeDiff),
1615 aInOutString);
1618 nsresult AppWindow::GetPersistentValue(const nsAtom* aAttr, nsAString& aValue) {
1619 if (!XRE_IsParentProcess()) {
1620 // The XULStore is only available in the parent process.
1621 return NS_ERROR_UNEXPECTED;
1624 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1625 if (!docShellElement) {
1626 return NS_ERROR_FAILURE;
1629 nsAutoString windowElementId;
1630 docShellElement->GetId(windowElementId);
1631 // Elements must have an ID to be persisted.
1632 if (windowElementId.IsEmpty()) {
1633 return NS_OK;
1636 RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
1637 nsIURI* docURI = ownerDoc->GetDocumentURI();
1638 if (!docURI) {
1639 return NS_ERROR_FAILURE;
1641 nsAutoCString utf8uri;
1642 nsresult rv = docURI->GetSpec(utf8uri);
1643 NS_ENSURE_SUCCESS(rv, rv);
1644 NS_ConvertUTF8toUTF16 uri(utf8uri);
1646 if (!mLocalStore) {
1647 mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1648 if (NS_WARN_IF(!mLocalStore)) {
1649 return NS_ERROR_NOT_INITIALIZED;
1653 rv = mLocalStore->GetValue(uri, windowElementId, nsDependentAtomString(aAttr),
1654 aValue);
1655 if (NS_WARN_IF(NS_FAILED(rv))) {
1656 return rv;
1659 if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
1660 // Convert attributes from outer size to inner size for top-level
1661 // windows, see bug 1444525 & co.
1662 ConvertWindowSize(this, aAttr, ConversionDirection::OuterToInner, aValue);
1665 return NS_OK;
1668 nsresult AppWindow::GetDocXulStoreKeys(nsString& aUriSpec,
1669 nsString& aWindowElementId) {
1670 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1671 if (!docShellElement) {
1672 return NS_ERROR_FAILURE;
1675 docShellElement->GetId(aWindowElementId);
1676 // Match the behavior of XULPersist and only persist values if the element
1677 // has an ID.
1678 if (aWindowElementId.IsEmpty()) {
1679 return NS_OK;
1682 RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
1683 nsIURI* docURI = ownerDoc->GetDocumentURI();
1684 if (!docURI) {
1685 return NS_ERROR_FAILURE;
1688 nsAutoCString utf8uri;
1689 nsresult rv = docURI->GetSpec(utf8uri);
1690 if (NS_WARN_IF(NS_FAILED(rv))) {
1691 return rv;
1694 aUriSpec = NS_ConvertUTF8toUTF16(utf8uri);
1696 return NS_OK;
1699 nsresult AppWindow::MaybeSaveEarlyWindowPersistentValues(
1700 const LayoutDeviceIntRect& aRect) {
1701 #ifdef XP_WIN
1702 nsAutoString uri;
1703 nsAutoString windowElementId;
1704 nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
1706 if (NS_WARN_IF(NS_FAILED(rv))) {
1707 return rv;
1710 if (!windowElementId.EqualsLiteral("main-window") ||
1711 !uri.EqualsLiteral("chrome://browser/content/browser.xhtml")) {
1712 return NS_OK;
1715 SkeletonUISettings settings;
1717 settings.screenX = aRect.X();
1718 settings.screenY = aRect.Y();
1719 settings.width = aRect.Width();
1720 settings.height = aRect.Height();
1722 settings.maximized = mWindow->SizeMode() == nsSizeMode_Maximized;
1723 settings.cssToDevPixelScaling = UnscaledDevicePixelsPerCSSPixel().scale;
1725 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1726 Document* doc = windowElement->GetComposedDoc();
1727 Element* urlbarEl = doc->GetElementById(u"urlbar"_ns);
1729 nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow();
1730 nsCOMPtr<nsIDOMWindowUtils> utils =
1731 nsGlobalWindowOuter::Cast(window)->WindowUtils();
1732 RefPtr<dom::DOMRect> urlbarRect;
1733 rv = utils->GetBoundsWithoutFlushing(urlbarEl, getter_AddRefs(urlbarRect));
1734 if (NS_WARN_IF(NS_FAILED(rv))) {
1735 return rv;
1738 double urlbarX = urlbarRect->X();
1739 double urlbarWidth = urlbarRect->Width();
1741 // Hard-coding the following values and this behavior in general is rather
1742 // fragile, and can easily get out of sync with the actual front-end values.
1743 // This is not intended as a long-term solution, but only as the relatively
1744 // straightforward implementation of an experimental feature. If we want to
1745 // ship the skeleton UI to all users, we should strongly consider a more
1746 // robust solution than this. The vertical position of the urlbar will be
1747 // fixed.
1748 nsAutoString attributeValue;
1749 urlbarEl->GetAttribute(u"breakout-extend"_ns, attributeValue);
1750 // Scale down the urlbar if it is focused
1751 if (attributeValue.EqualsLiteral("true")) {
1752 // defined in browser.inc.css as 2px
1753 int urlbarBreakoutExtend = 2;
1754 // defined in urlbar-searchbar.inc.css as 5px
1755 int urlbarMarginInline = 5;
1757 // breakout-extend measurements are defined in urlbar-searchbar.inc.css
1758 urlbarX += (double)(urlbarBreakoutExtend + urlbarMarginInline);
1759 urlbarWidth -= (double)(2 * (urlbarBreakoutExtend + urlbarMarginInline));
1761 CSSPixelSpan urlbar;
1762 urlbar.start = urlbarX;
1763 urlbar.end = urlbar.start + urlbarWidth;
1764 settings.urlbarSpan = urlbar;
1766 Element* navbar = doc->GetElementById(u"nav-bar"_ns);
1768 Element* searchbarEl = doc->GetElementById(u"searchbar"_ns);
1769 CSSPixelSpan searchbar;
1770 if (navbar->Contains(searchbarEl)) {
1771 RefPtr<dom::DOMRect> searchbarRect;
1772 rv = utils->GetBoundsWithoutFlushing(searchbarEl,
1773 getter_AddRefs(searchbarRect));
1774 if (NS_WARN_IF(NS_FAILED(rv))) {
1775 return rv;
1777 searchbar.start = searchbarRect->X();
1778 searchbar.end = searchbar.start + searchbarRect->Width();
1779 } else {
1780 // There is no searchbar in the UI
1781 searchbar.start = 0;
1782 searchbar.end = 0;
1784 settings.searchbarSpan = searchbar;
1786 nsAutoString bookmarksVisibility;
1787 Preferences::GetString("browser.toolbars.bookmarks.visibility",
1788 bookmarksVisibility);
1789 settings.bookmarksToolbarShown =
1790 bookmarksVisibility.EqualsLiteral("always") ||
1791 bookmarksVisibility.EqualsLiteral("newtab");
1793 Element* menubar = doc->GetElementById(u"toolbar-menubar"_ns);
1794 menubar->GetAttribute(u"autohide"_ns, attributeValue);
1795 settings.menubarShown = attributeValue.EqualsLiteral("false");
1797 ErrorResult err;
1798 nsCOMPtr<nsIHTMLCollection> toolbarSprings = navbar->GetElementsByTagNameNS(
1799 u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns,
1800 u"toolbarspring"_ns, err);
1801 if (err.Failed()) {
1802 return NS_ERROR_FAILURE;
1804 mozilla::Vector<CSSPixelSpan> springs;
1805 for (size_t i = 0; i < toolbarSprings->Length(); i++) {
1806 RefPtr<Element> springEl = toolbarSprings->Item(i);
1807 RefPtr<dom::DOMRect> springRect;
1808 rv = utils->GetBoundsWithoutFlushing(springEl, getter_AddRefs(springRect));
1809 if (NS_WARN_IF(NS_FAILED(rv))) {
1810 return rv;
1812 CSSPixelSpan spring;
1813 spring.start = springRect->X();
1814 spring.end = spring.start + springRect->Width();
1815 if (!settings.springs.append(spring)) {
1816 return NS_ERROR_FAILURE;
1820 settings.rtlEnabled = intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1822 bool isInTabletMode = false;
1823 bool autoTouchModePref =
1824 Preferences::GetBool("browser.touchmode.auto", false);
1825 if (autoTouchModePref) {
1826 nsCOMPtr<nsIWindowsUIUtils> uiUtils(
1827 do_GetService("@mozilla.org/windows-ui-utils;1"));
1828 if (!NS_WARN_IF(!uiUtils)) {
1829 uiUtils->GetInTabletMode(&isInTabletMode);
1833 if (isInTabletMode) {
1834 settings.uiDensity = SkeletonUIDensity::Touch;
1835 } else {
1836 int uiDensityPref = Preferences::GetInt("browser.uidensity", 0);
1837 switch (uiDensityPref) {
1838 case 0: {
1839 settings.uiDensity = SkeletonUIDensity::Default;
1840 break;
1842 case 1: {
1843 settings.uiDensity = SkeletonUIDensity::Compact;
1844 break;
1846 case 2: {
1847 settings.uiDensity = SkeletonUIDensity::Touch;
1848 break;
1853 Unused << PersistPreXULSkeletonUIValues(settings);
1854 #endif
1856 return NS_OK;
1859 nsresult AppWindow::SetPersistentValue(const nsAtom* aAttr,
1860 const nsAString& aValue) {
1861 if (!XRE_IsParentProcess()) {
1862 // The XULStore is only available in the parent process.
1863 return NS_ERROR_UNEXPECTED;
1866 nsAutoString uri;
1867 nsAutoString windowElementId;
1868 nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
1870 if (NS_FAILED(rv) || windowElementId.IsEmpty()) {
1871 return rv;
1874 nsAutoString maybeConvertedValue(aValue);
1875 if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
1876 // Make sure we store the <window> attributes as outer window size, see
1877 // bug 1444525 & co.
1878 ConvertWindowSize(this, aAttr, ConversionDirection::InnerToOuter,
1879 maybeConvertedValue);
1882 if (!mLocalStore) {
1883 mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1884 if (NS_WARN_IF(!mLocalStore)) {
1885 return NS_ERROR_NOT_INITIALIZED;
1889 return mLocalStore->SetValue(
1890 uri, windowElementId, nsDependentAtomString(aAttr), maybeConvertedValue);
1893 void AppWindow::MaybeSavePersistentPositionAndSize(
1894 PersistentAttributes aAttributes, Element& aRootElement,
1895 const nsAString& aPersistString, bool aShouldPersist) {
1896 if ((aAttributes & PersistentAttributes{PersistentAttribute::Position,
1897 PersistentAttribute::Size})
1898 .isEmpty()) {
1899 return;
1902 // get our size, position and mode to persist
1903 LayoutDeviceIntRect rect;
1904 if (NS_FAILED(mWindow->GetRestoredBounds(rect))) {
1905 return;
1908 // we use CSS pixels for size, but desktop pixels for position
1909 CSSToLayoutDeviceScale sizeScale = UnscaledDevicePixelsPerCSSPixel();
1910 DesktopToLayoutDeviceScale posScale = DevicePixelsPerDesktopPixel();
1912 // make our position relative to our parent, if any
1913 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
1914 if (parent) {
1915 int32_t parentX, parentY;
1916 if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
1917 rect.MoveBy(-parentX, -parentY);
1921 nsAutoString sizeString;
1922 // (only for size elements which are persisted)
1923 if (aAttributes.contains(PersistentAttribute::Position)) {
1924 if (aPersistString.Find(u"screenX") >= 0) {
1925 sizeString.Truncate();
1926 sizeString.AppendInt(NSToIntRound(rect.X() / posScale.scale));
1927 aRootElement.SetAttr(nsGkAtoms::screenX, sizeString, IgnoreErrors());
1928 if (aShouldPersist) {
1929 Unused << SetPersistentValue(nsGkAtoms::screenX, sizeString);
1932 if (aPersistString.Find(u"screenY") >= 0) {
1933 sizeString.Truncate();
1934 sizeString.AppendInt(NSToIntRound(rect.Y() / posScale.scale));
1935 aRootElement.SetAttr(nsGkAtoms::screenY, sizeString, IgnoreErrors());
1936 if (aShouldPersist) {
1937 Unused << SetPersistentValue(nsGkAtoms::screenY, sizeString);
1942 if (aAttributes.contains(PersistentAttribute::Size)) {
1943 LayoutDeviceIntRect innerRect =
1944 rect - GetOuterToInnerSizeDifference(mWindow);
1945 if (aPersistString.Find(u"width") >= 0) {
1946 sizeString.Truncate();
1947 sizeString.AppendInt(NSToIntRound(innerRect.Width() / sizeScale.scale));
1948 aRootElement.SetAttr(nsGkAtoms::width, sizeString, IgnoreErrors());
1949 if (aShouldPersist) {
1950 Unused << SetPersistentValue(nsGkAtoms::width, sizeString);
1953 if (aPersistString.Find(u"height") >= 0) {
1954 sizeString.Truncate();
1955 sizeString.AppendInt(NSToIntRound(innerRect.Height() / sizeScale.scale));
1956 aRootElement.SetAttr(nsGkAtoms::height, sizeString, IgnoreErrors());
1957 if (aShouldPersist) {
1958 Unused << SetPersistentValue(nsGkAtoms::height, sizeString);
1963 Unused << MaybeSaveEarlyWindowPersistentValues(rect);
1966 void AppWindow::MaybeSavePersistentMiscAttributes(
1967 PersistentAttributes aAttributes, Element& aRootElement,
1968 const nsAString& aPersistString, bool aShouldPersist) {
1969 if (!aAttributes.contains(PersistentAttribute::Misc)) {
1970 return;
1973 nsSizeMode sizeMode = mWindow->SizeMode();
1974 nsAutoString sizeString;
1975 if (sizeMode != nsSizeMode_Minimized) {
1976 if (sizeMode == nsSizeMode_Maximized) {
1977 sizeString.Assign(SIZEMODE_MAXIMIZED);
1978 } else if (sizeMode == nsSizeMode_Fullscreen) {
1979 sizeString.Assign(SIZEMODE_FULLSCREEN);
1980 } else {
1981 sizeString.Assign(SIZEMODE_NORMAL);
1983 aRootElement.SetAttr(nsGkAtoms::sizemode, sizeString, IgnoreErrors());
1984 if (aShouldPersist && aPersistString.Find(u"sizemode") >= 0) {
1985 Unused << SetPersistentValue(nsGkAtoms::sizemode, sizeString);
1988 aRootElement.SetAttribute(u"gtktiledwindow"_ns,
1989 mWindow->IsTiled() ? u"true"_ns : u"false"_ns,
1990 IgnoreErrors());
1993 void AppWindow::SavePersistentAttributes(
1994 const PersistentAttributes aAttributes) {
1995 // can happen when the persistence timer fires at an inopportune time
1996 // during window shutdown
1997 if (!mDocShell) {
1998 return;
2001 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
2002 if (!docShellElement) {
2003 return;
2006 nsAutoString persistString;
2007 docShellElement->GetAttr(nsGkAtoms::persist, persistString);
2008 if (persistString.IsEmpty()) { // quick check which sometimes helps
2009 mPersistentAttributesDirty.clear();
2010 return;
2013 bool shouldPersist = mWindow->SizeMode() != nsSizeMode_Fullscreen;
2014 MaybeSavePersistentPositionAndSize(aAttributes, *docShellElement,
2015 persistString, shouldPersist);
2016 MaybeSavePersistentMiscAttributes(aAttributes, *docShellElement,
2017 persistString, shouldPersist);
2018 mPersistentAttributesDirty -= aAttributes;
2021 NS_IMETHODIMP AppWindow::GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow) {
2022 NS_ENSURE_STATE(mDocShell);
2024 if (!mDOMWindow) mDOMWindow = mDocShell->GetWindow();
2025 NS_ENSURE_TRUE(mDOMWindow, NS_ERROR_FAILURE);
2027 *aDOMWindow = mDOMWindow;
2028 NS_ADDREF(*aDOMWindow);
2029 return NS_OK;
2032 dom::Element* AppWindow::GetWindowDOMElement() const {
2033 NS_ENSURE_TRUE(mDocShell, nullptr);
2035 nsCOMPtr<nsIDocumentViewer> viewer;
2036 mDocShell->GetDocViewer(getter_AddRefs(viewer));
2037 NS_ENSURE_TRUE(viewer, nullptr);
2039 const dom::Document* document = viewer->GetDocument();
2040 NS_ENSURE_TRUE(document, nullptr);
2042 return document->GetRootElement();
2045 nsresult AppWindow::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
2046 bool aPrimary) {
2047 // Set the default content tree owner
2048 if (aPrimary) {
2049 NS_ENSURE_SUCCESS(EnsurePrimaryContentTreeOwner(), NS_ERROR_FAILURE);
2050 aContentShell->SetTreeOwner(mPrimaryContentTreeOwner);
2051 mPrimaryContentShell = aContentShell;
2052 mPrimaryBrowserParent = nullptr;
2053 } else {
2054 NS_ENSURE_SUCCESS(EnsureContentTreeOwner(), NS_ERROR_FAILURE);
2055 aContentShell->SetTreeOwner(mContentTreeOwner);
2056 if (mPrimaryContentShell == aContentShell) mPrimaryContentShell = nullptr;
2059 return NS_OK;
2062 nsresult AppWindow::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
2063 if (mPrimaryContentShell == aContentShell) {
2064 mPrimaryContentShell = nullptr;
2066 return NS_OK;
2069 NS_IMETHODIMP
2070 AppWindow::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
2071 if (mPrimaryBrowserParent) {
2072 return GetPrimaryRemoteTabSize(aWidth, aHeight);
2074 if (mPrimaryContentShell) {
2075 return GetPrimaryContentShellSize(aWidth, aHeight);
2077 return NS_ERROR_UNEXPECTED;
2080 nsresult AppWindow::GetPrimaryRemoteTabSize(int32_t* aWidth, int32_t* aHeight) {
2081 BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
2082 // Need strong ref, since Client* can run script.
2083 RefPtr<dom::Element> element = host->GetOwnerElement();
2084 NS_ENSURE_STATE(element);
2086 CSSIntSize size(element->ClientWidth(), element->ClientHeight());
2087 LayoutDeviceIntSize sizeDev =
2088 RoundedToInt(size * UnscaledDevicePixelsPerCSSPixel());
2089 if (aWidth) {
2090 *aWidth = sizeDev.width;
2092 if (aHeight) {
2093 *aHeight = sizeDev.height;
2095 return NS_OK;
2098 nsresult AppWindow::GetPrimaryContentShellSize(int32_t* aWidth,
2099 int32_t* aHeight) {
2100 NS_ENSURE_STATE(mPrimaryContentShell);
2102 nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(mPrimaryContentShell));
2103 NS_ENSURE_STATE(shellWindow);
2105 LayoutDeviceIntSize sizeDev = shellWindow->GetSize();
2106 if (aWidth) {
2107 *aWidth = sizeDev.width;
2109 if (aHeight) {
2110 *aHeight = sizeDev.height;
2112 return NS_OK;
2115 NS_IMETHODIMP
2116 AppWindow::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
2117 if (mPrimaryBrowserParent) {
2118 return SetPrimaryRemoteTabSize(aWidth, aHeight);
2120 if (mPrimaryContentShell) {
2121 return SizeShellTo(mPrimaryContentShell, aWidth, aHeight);
2123 return NS_ERROR_UNEXPECTED;
2126 nsresult AppWindow::SetPrimaryRemoteTabSize(int32_t aWidth, int32_t aHeight) {
2127 int32_t shellWidth, shellHeight;
2128 GetPrimaryRemoteTabSize(&shellWidth, &shellHeight);
2129 SizeShellToWithLimit(aWidth, aHeight, shellWidth, shellHeight);
2130 return NS_OK;
2133 nsresult AppWindow::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
2134 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
2135 return mDocShell->GetSize(aWidth, aHeight);
2138 nsresult AppWindow::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
2139 return SizeShellTo(mDocShell, aWidth, aHeight);
2142 NS_IMETHODIMP AppWindow::SizeShellTo(nsIDocShellTreeItem* aShellItem,
2143 int32_t aCX, int32_t aCY) {
2144 MOZ_ASSERT(aShellItem == mDocShell || aShellItem == mPrimaryContentShell);
2145 if (aShellItem == mDocShell) {
2146 auto newSize =
2147 LayoutDeviceIntSize(aCX, aCY) + GetOuterToInnerSizeDifference(mWindow);
2148 SetSize(newSize.width, newSize.height, /* aRepaint = */ true);
2149 mDominantClientSize = true;
2150 return NS_OK;
2153 // XXXTAB This is wrong, we should actually reflow based on the passed in
2154 // shell. For now we are hacking and doing delta sizing. This is bad
2155 // because it assumes all size we add will go to the shell which probably
2156 // won't happen.
2157 nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
2158 NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
2160 int32_t width = 0;
2161 int32_t height = 0;
2162 shellAsWin->GetSize(&width, &height);
2164 SizeShellToWithLimit(aCX, aCY, width, height);
2166 return NS_OK;
2169 NS_IMETHODIMP AppWindow::ExitModalLoop(nsresult aStatus) {
2170 if (mContinueModalLoop) EnableParent(true);
2171 mContinueModalLoop = false;
2172 mModalStatus = aStatus;
2173 return NS_OK;
2176 // top-level function to create a new window
2177 NS_IMETHODIMP AppWindow::CreateNewWindow(int32_t aChromeFlags,
2178 nsIOpenWindowInfo* aOpenWindowInfo,
2179 nsIAppWindow** _retval) {
2180 NS_ENSURE_ARG_POINTER(_retval);
2182 if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME) {
2183 MOZ_RELEASE_ASSERT(
2184 !aOpenWindowInfo,
2185 "Unexpected nsOpenWindowInfo when creating a new chrome window");
2186 return CreateNewChromeWindow(aChromeFlags, _retval);
2189 return CreateNewContentWindow(aChromeFlags, aOpenWindowInfo, _retval);
2192 NS_IMETHODIMP AppWindow::CreateNewChromeWindow(int32_t aChromeFlags,
2193 nsIAppWindow** _retval) {
2194 nsCOMPtr<nsIAppShellService> appShell(
2195 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
2196 NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
2198 // Just do a normal create of a window and return.
2199 nsCOMPtr<nsIAppWindow> newWindow;
2200 appShell->CreateTopLevelWindow(
2201 this, nullptr, aChromeFlags, nsIAppShellService::SIZE_TO_CONTENT,
2202 nsIAppShellService::SIZE_TO_CONTENT, getter_AddRefs(newWindow));
2204 NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
2206 newWindow.forget(_retval);
2208 return NS_OK;
2211 NS_IMETHODIMP AppWindow::CreateNewContentWindow(
2212 int32_t aChromeFlags, nsIOpenWindowInfo* aOpenWindowInfo,
2213 nsIAppWindow** _retval) {
2214 nsCOMPtr<nsIAppShellService> appShell(
2215 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
2216 NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
2218 // We need to create a new top level window and then enter a nested
2219 // loop. Eventually the new window will be told that it has loaded,
2220 // at which time we know it is safe to spin out of the nested loop
2221 // and allow the opening code to proceed.
2223 nsCOMPtr<nsIURI> uri;
2224 nsAutoCString urlStr;
2225 urlStr.AssignLiteral(BROWSER_CHROME_URL_QUOTED);
2227 nsCOMPtr<nsIIOService> service(do_GetService(NS_IOSERVICE_CONTRACTID));
2228 if (service) {
2229 service->NewURI(urlStr, nullptr, nullptr, getter_AddRefs(uri));
2231 NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
2233 // We need to create a chrome window to contain the content window we're about
2234 // to pass back. The subject principal needs to be system while we're creating
2235 // it to make things work right, so force a system caller. See bug 799348
2236 // comment 13 for a description of what happens when we don't.
2237 nsCOMPtr<nsIAppWindow> newWindow;
2239 AutoNoJSAPI nojsapi;
2240 appShell->CreateTopLevelWindow(this, uri, aChromeFlags, 615, 480,
2241 getter_AddRefs(newWindow));
2242 NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
2245 AppWindow* appWin =
2246 static_cast<AppWindow*>(static_cast<nsIAppWindow*>(newWindow));
2248 // Specify which flags should be used by browser.xhtml to create the initial
2249 // content browser window.
2250 appWin->mInitialOpenWindowInfo = aOpenWindowInfo;
2252 // Specify that we want the window to remain locked until the chrome has
2253 // loaded.
2254 appWin->LockUntilChromeLoad();
2257 AutoNoJSAPI nojsapi;
2258 SpinEventLoopUntil("AppWindow::CreateNewContentWindow"_ns,
2259 [&]() { return !appWin->IsLocked(); });
2262 NS_ENSURE_STATE(appWin->mPrimaryContentShell ||
2263 appWin->mPrimaryBrowserParent);
2264 MOZ_ASSERT_IF(appWin->mPrimaryContentShell,
2265 !aOpenWindowInfo->GetNextRemoteBrowser());
2267 newWindow.forget(_retval);
2269 return NS_OK;
2272 NS_IMETHODIMP AppWindow::GetHasPrimaryContent(bool* aResult) {
2273 *aResult = mPrimaryBrowserParent || mPrimaryContentShell;
2274 return NS_OK;
2277 void AppWindow::EnableParent(bool aEnable) {
2278 nsCOMPtr<nsIBaseWindow> parentWindow;
2279 nsCOMPtr<nsIWidget> parentWidget;
2281 parentWindow = do_QueryReferent(mParentWindow);
2282 if (parentWindow) parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
2283 if (parentWidget) parentWidget->Enable(aEnable);
2286 void AppWindow::SetContentScrollbarVisibility(bool aVisible) {
2287 nsCOMPtr<nsPIDOMWindowOuter> contentWin(
2288 do_GetInterface(mPrimaryContentShell));
2289 if (!contentWin) {
2290 return;
2293 nsContentUtils::SetScrollbarsVisibility(contentWin->GetDocShell(), aVisible);
2296 void AppWindow::ApplyChromeFlags() {
2297 nsCOMPtr<dom::Element> window = GetWindowDOMElement();
2298 if (!window) {
2299 return;
2302 if (mChromeLoaded) {
2303 // The two calls in this block don't need to happen early because they
2304 // don't cause a global restyle on the document. Not only that, but the
2305 // scrollbar stuff needs a content area to toggle the scrollbars on anyway.
2306 // So just don't do these until mChromeLoaded is true.
2308 // Scrollbars have their own special treatment.
2309 SetContentScrollbarVisibility(mChromeFlags &
2310 nsIWebBrowserChrome::CHROME_SCROLLBARS);
2313 /* the other flags are handled together. we have style rules
2314 in navigator.css that trigger visibility based on
2315 the 'chromehidden' attribute of the <window> tag. */
2316 nsAutoString newvalue;
2318 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_MENUBAR))
2319 newvalue.AppendLiteral("menubar ");
2321 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_TOOLBAR))
2322 newvalue.AppendLiteral("toolbar ");
2324 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_LOCATIONBAR))
2325 newvalue.AppendLiteral("location ");
2327 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR))
2328 newvalue.AppendLiteral("directories ");
2330 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_STATUSBAR))
2331 newvalue.AppendLiteral("status ");
2333 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_EXTRA))
2334 newvalue.AppendLiteral("extrachrome ");
2336 // Note that if we're not actually changing the value this will be a no-op,
2337 // so no need to compare to the old value.
2338 IgnoredErrorResult rv;
2339 window->SetAttribute(u"chromehidden"_ns, newvalue, rv);
2342 NS_IMETHODIMP
2343 AppWindow::BeforeStartLayout() {
2344 ApplyChromeFlags();
2345 // Ordering here is important, loading width/height values in
2346 // LoadPersistentWindowState() depends on the chromemargin attribute (since
2347 // we need to translate outer to inner sizes).
2348 SyncAttributesToWidget();
2349 LoadPersistentWindowState();
2350 if (mWindow) {
2351 SizeShell();
2353 return NS_OK;
2356 NS_IMETHODIMP
2357 AppWindow::LockAspectRatio(bool aShouldLock) {
2358 mWindow->LockAspectRatio(aShouldLock);
2359 return NS_OK;
2362 NS_IMETHODIMP
2363 AppWindow::NeedFastSnaphot() {
2364 MOZ_ASSERT(mWindow);
2365 if (!mWindow) {
2366 return NS_ERROR_FAILURE;
2368 mWindow->SetNeedFastSnaphot();
2369 return NS_OK;
2372 void AppWindow::LoadPersistentWindowState() {
2373 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
2374 if (!docShellElement) {
2375 return;
2378 // Check if the window wants to persist anything.
2379 nsAutoString persist;
2380 docShellElement->GetAttr(nsGkAtoms::persist, persist);
2381 if (persist.IsEmpty()) {
2382 return;
2385 auto loadValue = [&](nsAtom* aAttr) {
2386 nsDependentAtomString attrString(aAttr);
2387 if (persist.Find(attrString) >= 0) {
2388 nsAutoString value;
2389 nsresult rv = GetPersistentValue(aAttr, value);
2390 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get persistent state.");
2391 if (NS_SUCCEEDED(rv) && !value.IsEmpty()) {
2392 docShellElement->SetAttr(aAttr, value, IgnoreErrors());
2397 loadValue(nsGkAtoms::screenX);
2398 loadValue(nsGkAtoms::screenY);
2399 loadValue(nsGkAtoms::width);
2400 loadValue(nsGkAtoms::height);
2401 loadValue(nsGkAtoms::sizemode);
2404 void AppWindow::IntrinsicallySizeShell(const CSSIntSize& aWindowDiff,
2405 int32_t& aSpecWidth,
2406 int32_t& aSpecHeight) {
2407 nsCOMPtr<nsIDocumentViewer> viewer;
2408 mDocShell->GetDocViewer(getter_AddRefs(viewer));
2409 if (!viewer) {
2410 return;
2412 RefPtr<nsDocShell> docShell = mDocShell;
2414 CSSIntCoord maxWidth = 0;
2415 CSSIntCoord maxHeight = 0;
2416 CSSIntCoord prefWidth = 0;
2417 if (RefPtr element = GetWindowDOMElement()) {
2418 nsAutoString prefWidthAttr;
2419 if (element->GetAttr(nsGkAtoms::prefwidth, prefWidthAttr)) {
2420 // TODO: Make this more generic perhaps?
2421 if (prefWidthAttr.EqualsLiteral("min-width")) {
2422 if (auto* f = element->GetPrimaryFrame(FlushType::Frames)) {
2423 const auto& coord = f->StylePosition()->mMinWidth;
2424 if (coord.ConvertsToLength()) {
2425 prefWidth = CSSPixel::FromAppUnitsRounded(coord.ToLength());
2432 Maybe<CSSIntSize> size =
2433 viewer->GetContentSize(maxWidth, maxHeight, prefWidth);
2434 if (!size) {
2435 return;
2437 nsPresContext* pc = viewer->GetPresContext();
2438 MOZ_ASSERT(pc, "Should have pres context");
2440 int32_t width = pc->CSSPixelsToDevPixels(size->width);
2441 int32_t height = pc->CSSPixelsToDevPixels(size->height);
2442 SizeShellTo(docShell, width, height);
2444 // Update specified size for the final LoadPositionFromXUL call.
2445 aSpecWidth = size->width + aWindowDiff.width;
2446 aSpecHeight = size->height + aWindowDiff.height;
2449 void AppWindow::SizeShell() {
2450 AutoRestore<bool> sizingShellFromXUL(mSizingShellFromXUL);
2451 mSizingShellFromXUL = true;
2453 int32_t specWidth = -1, specHeight = -1;
2454 bool gotSize = false;
2456 nsAutoString windowType;
2457 if (nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement()) {
2458 windowElement->GetAttr(nsGkAtoms::windowtype, windowType);
2461 const CSSIntSize windowDiff = GetOuterToInnerSizeDifferenceInCSSPixels(
2462 mWindow, UnscaledDevicePixelsPerCSSPixel());
2464 // If we're using fingerprint resistance, we're going to resize the window
2465 // once we have primary content.
2466 if (nsContentUtils::ShouldResistFingerprinting(
2467 "if RFP is enabled we want to round the dimensions of the new"
2468 "new pop up window regardless of their origin",
2469 RFPTarget::RoundWindowSize) &&
2470 windowType.EqualsLiteral("navigator:browser")) {
2471 // Once we've got primary content, force dimensions.
2472 if (mPrimaryContentShell || mPrimaryBrowserParent) {
2473 ForceRoundedDimensions();
2475 // Always avoid setting size/sizemode on this window.
2476 mIgnoreXULSize = true;
2477 mIgnoreXULSizeMode = true;
2478 } else if (!mIgnoreXULSize) {
2479 gotSize = LoadSizeFromXUL(specWidth, specHeight);
2480 specWidth += windowDiff.width;
2481 specHeight += windowDiff.height;
2484 bool positionSet = !mIgnoreXULPosition;
2485 nsCOMPtr<nsIAppWindow> parentWindow(do_QueryReferent(mParentWindow));
2486 #if defined(XP_UNIX) && !defined(XP_MACOSX)
2487 // don't override WM placement on unix for independent, top-level windows
2488 // (however, we think the benefits of intelligent dependent window placement
2489 // trump that override.)
2490 if (!parentWindow) positionSet = false;
2491 #endif
2492 if (positionSet) {
2493 // We have to do this before sizing the window, because sizing depends
2494 // on the resolution of the screen we're on. But positioning needs to
2495 // know the size so that it can constrain to screen bounds.... as an
2496 // initial guess here, we'll use the specified size (if any).
2497 positionSet = LoadPositionFromXUL(specWidth, specHeight);
2500 if (gotSize) {
2501 SetSpecifiedSize(specWidth, specHeight);
2504 // If LoadSizeFromXUL set the size, mIntrinsicallySized will be false.
2505 if (mIntrinsicallySized) {
2506 IntrinsicallySizeShell(windowDiff, specWidth, specHeight);
2509 // Now that we have set the window's final size, we can re-do its
2510 // positioning so that it is properly constrained to the screen.
2511 if (positionSet) {
2512 LoadPositionFromXUL(specWidth, specHeight);
2515 UpdateWindowStateFromMiscXULAttributes();
2517 if (mChromeLoaded && mCenterAfterLoad && !positionSet &&
2518 mWindow->SizeMode() == nsSizeMode_Normal) {
2519 Center(parentWindow, parentWindow ? false : true, false);
2523 NS_IMETHODIMP AppWindow::GetXULBrowserWindow(
2524 nsIXULBrowserWindow** aXULBrowserWindow) {
2525 NS_IF_ADDREF(*aXULBrowserWindow = mXULBrowserWindow);
2526 return NS_OK;
2529 NS_IMETHODIMP AppWindow::SetXULBrowserWindow(
2530 nsIXULBrowserWindow* aXULBrowserWindow) {
2531 mXULBrowserWindow = aXULBrowserWindow;
2532 return NS_OK;
2535 // Given the dimensions of some content area held within this XUL window, and
2536 // assuming that that content area will change its dimensions in linear
2537 // proportion to the dimensions of this XUL window, changes the size of the XUL
2538 // window so that the content area reaches a particular size.
2539 void AppWindow::SizeShellToWithLimit(int32_t aDesiredWidth,
2540 int32_t aDesiredHeight,
2541 int32_t shellItemWidth,
2542 int32_t shellItemHeight) {
2543 int32_t widthDelta = aDesiredWidth - shellItemWidth;
2544 int32_t heightDelta = aDesiredHeight - shellItemHeight;
2546 int32_t winWidth = 0;
2547 int32_t winHeight = 0;
2549 GetSize(&winWidth, &winHeight);
2550 // There's no point in trying to make the window smaller than the
2551 // desired content area size --- that's not likely to work. This whole
2552 // function assumes that the outer docshell is adding some constant
2553 // "border" chrome to the content area.
2554 winWidth = std::max(winWidth + widthDelta, aDesiredWidth);
2555 winHeight = std::max(winHeight + heightDelta, aDesiredHeight);
2557 // Note: Because of the asynchronous resizing on Linux we have to call
2558 // SetSize even when the size doesn't appear to change. A previous call that
2559 // has yet to complete can still change the size. We want the latest call to
2560 // define the final size.
2561 SetSize(winWidth, winHeight, true);
2562 mDominantClientSize = true;
2565 nsresult AppWindow::GetTabCount(uint32_t* aResult) {
2566 if (mXULBrowserWindow) {
2567 return mXULBrowserWindow->GetTabCount(aResult);
2570 *aResult = 0;
2571 return NS_OK;
2574 nsresult AppWindow::GetInitialOpenWindowInfo(
2575 nsIOpenWindowInfo** aOpenWindowInfo) {
2576 NS_ENSURE_ARG_POINTER(aOpenWindowInfo);
2577 *aOpenWindowInfo = do_AddRef(mInitialOpenWindowInfo).take();
2578 return NS_OK;
2581 PresShell* AppWindow::GetPresShell() {
2582 if (!mDocShell) {
2583 return nullptr;
2585 return mDocShell->GetPresShell();
2588 bool AppWindow::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) {
2589 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
2590 if (pm) {
2591 nsCOMPtr<nsPIDOMWindowOuter> window =
2592 mDocShell ? mDocShell->GetWindow() : nullptr;
2593 pm->AdjustPopupsOnWindowChange(window);
2596 // Notify all tabs that the widget moved.
2597 if (mDocShell && mDocShell->GetWindow()) {
2598 nsCOMPtr<EventTarget> eventTarget =
2599 mDocShell->GetWindow()->GetTopWindowRoot();
2600 nsContentUtils::DispatchChromeEvent(
2601 mDocShell->GetDocument(), eventTarget, u"MozUpdateWindowPos"_ns,
2602 CanBubble::eNo, Cancelable::eNo, nullptr);
2605 // Persist position, but not immediately, in case this OS is firing
2606 // repeated move events as the user drags the window
2607 PersistentAttributesDirty(PersistentAttribute::Position, Async);
2608 return false;
2611 bool AppWindow::WindowResized(nsIWidget* aWidget, int32_t aWidth,
2612 int32_t aHeight) {
2613 mDominantClientSize = false;
2614 if (mDocShell) {
2615 mDocShell->SetPositionAndSize(0, 0, aWidth, aHeight, 0);
2617 // Persist size, but not immediately, in case this OS is firing
2618 // repeated size events as the user drags the sizing handle
2619 if (!IsLocked()) {
2620 PersistentAttributesDirty(AllPersistentAttributes(), Async);
2622 // Check if we need to continue a fullscreen change.
2623 switch (mFullscreenChangeState) {
2624 case FullscreenChangeState::WillChange:
2625 mFullscreenChangeState = FullscreenChangeState::WidgetResized;
2626 break;
2627 case FullscreenChangeState::WidgetEnteredFullscreen:
2628 FinishFullscreenChange(true);
2629 break;
2630 case FullscreenChangeState::WidgetExitedFullscreen:
2631 FinishFullscreenChange(false);
2632 break;
2633 case FullscreenChangeState::WidgetResized:
2634 case FullscreenChangeState::NotChanging:
2635 break;
2637 return true;
2640 bool AppWindow::RequestWindowClose(nsIWidget* aWidget) {
2641 // Maintain a reference to this as it is about to get destroyed.
2642 nsCOMPtr<nsIAppWindow> appWindow(this);
2644 nsCOMPtr<nsPIDOMWindowOuter> window(mDocShell ? mDocShell->GetWindow()
2645 : nullptr);
2646 nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window);
2648 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
2649 if (!presShell) {
2650 mozilla::DebugOnly<bool> dying;
2651 MOZ_ASSERT(NS_SUCCEEDED(mDocShell->IsBeingDestroyed(&dying)) && dying,
2652 "No presShell, but window is not being destroyed");
2653 } else if (eventTarget) {
2654 RefPtr<nsPresContext> presContext = presShell->GetPresContext();
2656 nsEventStatus status = nsEventStatus_eIgnore;
2657 WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
2658 if (NS_SUCCEEDED(EventDispatcher::Dispatch(eventTarget, presContext, &event,
2659 nullptr, &status)) &&
2660 status == nsEventStatus_eConsumeNoDefault)
2661 return false;
2664 Destroy();
2665 return false;
2668 void AppWindow::SizeModeChanged(nsSizeMode aSizeMode) {
2669 const bool wasWidgetInFullscreen = mIsWidgetInFullscreen;
2670 // Fullscreen and minimized states are usually compatible, and the widget
2671 // typically returns to fullscreen after restoration. By not updating the
2672 // widget's fullscreen state while it is minimized, we can avoid unnecessary
2673 // fullscreen exits, such as those encountered in bug 1823284.
2674 if (aSizeMode != nsSizeMode_Minimized) {
2675 mIsWidgetInFullscreen = aSizeMode == nsSizeMode_Fullscreen;
2678 const bool fullscreenChanged = wasWidgetInFullscreen != mIsWidgetInFullscreen;
2679 if (fullscreenChanged) {
2680 FullscreenWillChange(mIsWidgetInFullscreen);
2683 RecomputeBrowsingContextVisibility();
2685 PersistentAttributesDirty(PersistentAttribute::Misc, Sync);
2686 nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
2687 mDocShell ? mDocShell->GetWindow() : nullptr;
2688 if (ourWindow) {
2689 // Always fire a user-defined sizemodechange event on the window
2690 ourWindow->DispatchCustomEvent(u"sizemodechange"_ns);
2693 if (PresShell* presShell = GetPresShell()) {
2694 presShell->GetPresContext()->SizeModeChanged(aSizeMode);
2697 if (fullscreenChanged) {
2698 FullscreenChanged(mIsWidgetInFullscreen);
2701 // Note the current implementation of SetSizeMode just stores
2702 // the new state; it doesn't actually resize. So here we store
2703 // the state and pass the event on to the OS. The day is coming
2704 // when we'll handle the event here, and the return result will
2705 // then need to be different.
2708 void AppWindow::FullscreenWillChange(bool aInFullscreen) {
2709 if (mDocShell) {
2710 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2711 ourWindow->FullscreenWillChange(aInFullscreen);
2714 MOZ_ASSERT(mFullscreenChangeState == FullscreenChangeState::NotChanging);
2716 CSSToLayoutDeviceScale scale = UnscaledDevicePixelsPerCSSPixel();
2717 CSSIntSize windowSizeCSS = RoundedToInt(GetSize() / scale);
2719 CSSIntSize screenSizeCSS;
2720 GetAvailScreenSize(&screenSizeCSS.width, &screenSizeCSS.height);
2722 // Check if the window is already at the expected dimensions. If it is, set
2723 // the fullscreen change state to WidgetResized to avoid waiting for a resize
2724 // event. On macOS, a fullscreen window could be slightly higher than
2725 // available screen size because of the OS menu bar isn't yet hidden.
2726 mFullscreenChangeState =
2727 (aInFullscreen == (windowSizeCSS.width == screenSizeCSS.width &&
2728 windowSizeCSS.height >= screenSizeCSS.height))
2729 ? FullscreenChangeState::WidgetResized
2730 : FullscreenChangeState::WillChange;
2733 void AppWindow::FullscreenChanged(bool aInFullscreen) {
2734 if (mFullscreenChangeState == FullscreenChangeState::WidgetResized) {
2735 FinishFullscreenChange(aInFullscreen);
2736 } else {
2737 NS_WARNING_ASSERTION(
2738 mFullscreenChangeState == FullscreenChangeState::WillChange,
2739 "Unexpected fullscreen change state");
2740 FullscreenChangeState newState =
2741 aInFullscreen ? FullscreenChangeState::WidgetEnteredFullscreen
2742 : FullscreenChangeState::WidgetExitedFullscreen;
2743 mFullscreenChangeState = newState;
2744 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
2745 // Wait for resize for a small amount of time.
2746 // 80ms is actually picked arbitrarily. But it shouldn't be too large
2747 // in case the widget resize is not going to happen at all, which can
2748 // be the case for some Linux window managers and possibly Android.
2749 NS_DelayedDispatchToCurrentThread(
2750 NS_NewRunnableFunction(
2751 "AppWindow::FullscreenChanged",
2752 [this, kungFuDeathGrip, newState, aInFullscreen]() {
2753 if (mFullscreenChangeState == newState) {
2754 FinishFullscreenChange(aInFullscreen);
2757 80);
2761 void AppWindow::FinishFullscreenChange(bool aInFullscreen) {
2762 mFullscreenChangeState = FullscreenChangeState::NotChanging;
2763 if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
2764 pm->Rollup({});
2766 if (mDocShell) {
2767 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2768 ourWindow->FinishFullscreenChange(aInFullscreen);
2773 void AppWindow::MacFullscreenMenubarOverlapChanged(
2774 mozilla::DesktopCoord aOverlapAmount) {
2775 if (mDocShell) {
2776 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2777 ourWindow->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
2782 void AppWindow::RecomputeBrowsingContextVisibility() {
2783 if (!mDocShell) {
2784 return;
2786 RefPtr bc = mDocShell->GetBrowsingContext();
2787 if (!bc) {
2788 return;
2790 bc->Canonical()->RecomputeAppWindowVisibility();
2793 void AppWindow::OcclusionStateChanged(bool aIsFullyOccluded) {
2794 if (!mDocShell) {
2795 return;
2797 RecomputeBrowsingContextVisibility();
2798 if (RefPtr win = mDocShell->GetWindow()) {
2799 // And always fire a user-defined occlusionstatechange event on the window
2800 win->DispatchCustomEvent(u"occlusionstatechange"_ns,
2801 ChromeOnlyDispatch::eYes);
2805 void AppWindow::OSToolbarButtonPressed() {
2806 // Keep a reference as setting the chrome flags can fire events.
2807 nsCOMPtr<nsIAppWindow> appWindow(this);
2809 // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
2810 // due to components with multiple sidebar components
2811 // (such as Mail/News, Addressbook, etc)... and frankly,
2812 // Mac IE, OmniWeb, and other Mac OS X apps all work this way
2813 uint32_t chromeMask = (nsIWebBrowserChrome::CHROME_TOOLBAR |
2814 nsIWebBrowserChrome::CHROME_LOCATIONBAR |
2815 nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
2817 nsCOMPtr<nsIWebBrowserChrome> wbc(do_GetInterface(appWindow));
2818 if (!wbc) return;
2820 uint32_t chromeFlags, newChromeFlags = 0;
2821 wbc->GetChromeFlags(&chromeFlags);
2822 newChromeFlags = chromeFlags & chromeMask;
2823 if (!newChromeFlags)
2824 chromeFlags |= chromeMask;
2825 else
2826 chromeFlags &= (~newChromeFlags);
2827 wbc->SetChromeFlags(chromeFlags);
2830 void AppWindow::WindowActivated() {
2831 nsCOMPtr<nsIAppWindow> appWindow(this);
2833 // focusing the window could cause it to close, so keep a reference to it
2834 if (mDocShell) {
2835 if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
2836 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
2837 fm->WindowRaised(window, nsFocusManager::GenerateFocusActionId());
2842 if (mChromeLoaded) {
2843 PersistentAttributesDirty(AllPersistentAttributes(), Sync);
2847 void AppWindow::WindowDeactivated() {
2848 if (mDocShell) {
2849 if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
2850 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
2851 if (!fm->IsTestMode()) {
2852 fm->WindowLowered(window, nsFocusManager::GenerateFocusActionId());
2859 #ifdef USE_NATIVE_MENUS
2861 struct LoadNativeMenusListener {
2862 LoadNativeMenusListener(Document* aDoc, nsIWidget* aParentWindow)
2863 : mDocument(aDoc), mParentWindow(aParentWindow) {}
2865 RefPtr<Document> mDocument;
2866 nsCOMPtr<nsIWidget> mParentWindow;
2869 // On macOS the hidden window is created eagerly, and we want to wait for it to
2870 // load the native menus.
2871 static bool sWaitingForHiddenWindowToLoadNativeMenus =
2872 # ifdef XP_MACOSX
2873 true
2874 # else
2875 false
2876 # endif
2879 static nsTArray<LoadNativeMenusListener> sLoadNativeMenusListeners;
2881 static void BeginLoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow);
2883 static void LoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
2884 MOZ_ASSERT(!gfxPlatform::IsHeadless());
2886 // Find the menubar tag (if there is more than one, we ignore all but
2887 // the first).
2888 nsCOMPtr<nsINodeList> menubarElements = aDoc->GetElementsByTagNameNS(
2889 u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns,
2890 u"menubar"_ns);
2892 RefPtr<Element> menubar;
2893 if (menubarElements) {
2894 menubar = Element::FromNodeOrNull(menubarElements->Item(0));
2897 widget::NativeMenuSupport::CreateNativeMenuBar(aParentWindow, menubar);
2899 if (sWaitingForHiddenWindowToLoadNativeMenus) {
2900 sWaitingForHiddenWindowToLoadNativeMenus = false;
2901 for (auto& listener : sLoadNativeMenusListeners) {
2902 BeginLoadNativeMenus(listener.mDocument, listener.mParentWindow);
2904 sLoadNativeMenusListeners.Clear();
2908 class L10nReadyPromiseHandler final : public dom::PromiseNativeHandler {
2909 public:
2910 NS_DECL_ISUPPORTS
2912 L10nReadyPromiseHandler(Document* aDoc, nsIWidget* aParentWindow)
2913 : mDocument(aDoc), mWindow(aParentWindow) {}
2915 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
2916 ErrorResult& aRv) override {
2917 LoadNativeMenus(mDocument, mWindow);
2920 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
2921 ErrorResult& aRv) override {
2922 // Again, this shouldn't happen, but fallback to loading the menus as is.
2923 NS_WARNING(
2924 "L10nReadyPromiseHandler rejected - loading fallback native "
2925 "menu.");
2926 LoadNativeMenus(mDocument, mWindow);
2929 private:
2930 ~L10nReadyPromiseHandler() = default;
2932 RefPtr<Document> mDocument;
2933 nsCOMPtr<nsIWidget> mWindow;
2936 NS_IMPL_ISUPPORTS0(L10nReadyPromiseHandler)
2938 static void BeginLoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
2939 if (RefPtr<DocumentL10n> l10n = aDoc->GetL10n()) {
2940 // Wait for l10n to be ready so the menus are localized.
2941 RefPtr<Promise> promise = l10n->Ready();
2942 MOZ_ASSERT(promise);
2943 RefPtr handler = new L10nReadyPromiseHandler(aDoc, aParentWindow);
2944 promise->AppendNativeHandler(handler);
2945 } else {
2946 // Something went wrong loading the doc and l10n wasn't created. This
2947 // shouldn't really happen, but if it does fallback to trying to load
2948 // the menus as is.
2949 LoadNativeMenus(aDoc, aParentWindow);
2953 #endif
2955 class AppWindowTimerCallback final : public nsITimerCallback, public nsINamed {
2956 public:
2957 explicit AppWindowTimerCallback(AppWindow* aWindow) : mWindow(aWindow) {}
2959 NS_DECL_THREADSAFE_ISUPPORTS
2961 NS_IMETHOD Notify(nsITimer* aTimer) override {
2962 // Although this object participates in a refcount cycle (this -> mWindow
2963 // -> mSPTimer -> this), mSPTimer is a one-shot timer and releases this
2964 // after it fires. So we don't need to release mWindow here.
2966 mWindow->FirePersistenceTimer();
2967 return NS_OK;
2970 NS_IMETHOD GetName(nsACString& aName) override {
2971 aName.AssignLiteral("AppWindowTimerCallback");
2972 return NS_OK;
2975 private:
2976 ~AppWindowTimerCallback() {}
2978 RefPtr<AppWindow> mWindow;
2981 NS_IMPL_ISUPPORTS(AppWindowTimerCallback, nsITimerCallback, nsINamed)
2983 void AppWindow::PersistentAttributesDirty(PersistentAttributes aAttributes,
2984 PersistentAttributeUpdate aUpdate) {
2985 aAttributes = aAttributes & mPersistentAttributesMask;
2986 if (aAttributes.isEmpty()) {
2987 return;
2990 mPersistentAttributesDirty += aAttributes;
2991 if (aUpdate == Sync) {
2992 // Only apply the attributes we've been requested to apply sync, not other
2993 // potentially dirty attributes that have been requested asynchronously.
2994 SavePersistentAttributes(aAttributes);
2995 return;
2997 if (!mSPTimer) {
2998 mSPTimer = NS_NewTimer();
2999 if (!mSPTimer) {
3000 NS_WARNING("Couldn't create timer instance?");
3001 return;
3005 RefPtr<AppWindowTimerCallback> callback = new AppWindowTimerCallback(this);
3006 mSPTimer->InitWithCallback(callback, SIZE_PERSISTENCE_TIMEOUT,
3007 nsITimer::TYPE_ONE_SHOT);
3010 void AppWindow::FirePersistenceTimer() { SavePersistentAttributes(); }
3012 //----------------------------------------
3013 // nsIWebProgessListener implementation
3014 //----------------------------------------
3015 NS_IMETHODIMP
3016 AppWindow::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3017 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
3018 int32_t aCurTotalProgress,
3019 int32_t aMaxTotalProgress) {
3020 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3021 return NS_OK;
3024 NS_IMETHODIMP
3025 AppWindow::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3026 uint32_t aStateFlags, nsresult aStatus) {
3027 // If the notification is not about a document finishing, then just
3028 // ignore it...
3029 if (!(aStateFlags & nsIWebProgressListener::STATE_STOP) ||
3030 !(aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) {
3031 return NS_OK;
3034 if (mChromeLoaded) return NS_OK;
3036 // If this document notification is for a frame then ignore it...
3037 nsCOMPtr<mozIDOMWindowProxy> eventWin;
3038 aProgress->GetDOMWindow(getter_AddRefs(eventWin));
3039 auto* eventPWin = nsPIDOMWindowOuter::From(eventWin);
3040 if (eventPWin) {
3041 nsPIDOMWindowOuter* rootPWin = eventPWin->GetPrivateRoot();
3042 if (eventPWin != rootPWin) return NS_OK;
3045 mChromeLoaded = true;
3046 mLockedUntilChromeLoad = false;
3048 #ifdef USE_NATIVE_MENUS
3049 ///////////////////////////////
3050 // Find the Menubar DOM and Load the menus, hooking them up to the loaded
3051 // commands
3052 ///////////////////////////////
3053 if (!gfxPlatform::IsHeadless()) {
3054 if (RefPtr<Document> menubarDoc = mDocShell->GetExtantDocument()) {
3055 if (mIsHiddenWindow || !sWaitingForHiddenWindowToLoadNativeMenus) {
3056 BeginLoadNativeMenus(menubarDoc, mWindow);
3057 } else {
3058 sLoadNativeMenusListeners.EmplaceBack(menubarDoc, mWindow);
3062 #endif // USE_NATIVE_MENUS
3064 OnChromeLoaded();
3066 return NS_OK;
3069 NS_IMETHODIMP
3070 AppWindow::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3071 nsIURI* aURI, uint32_t aFlags) {
3072 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3073 return NS_OK;
3076 NS_IMETHODIMP
3077 AppWindow::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3078 nsresult aStatus, const char16_t* aMessage) {
3079 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3080 return NS_OK;
3083 NS_IMETHODIMP
3084 AppWindow::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3085 uint32_t aState) {
3086 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3087 return NS_OK;
3090 NS_IMETHODIMP
3091 AppWindow::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
3092 nsIRequest* aRequest, uint32_t aEvent) {
3093 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3094 return NS_OK;
3098 * ExecuteCloseHandler - Run the close handler, if any.
3099 * @return true iff we found a close handler to run.
3101 bool AppWindow::ExecuteCloseHandler() {
3102 /* If the event handler closes this window -- a likely scenario --
3103 things get deleted out of order without this death grip.
3104 (The problem may be the death grip in nsWindow::windowProc,
3105 which forces this window's widget to remain alive longer
3106 than it otherwise would.) */
3107 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
3109 nsCOMPtr<EventTarget> eventTarget;
3110 if (mDocShell) {
3111 eventTarget = do_QueryInterface(mDocShell->GetWindow());
3114 if (eventTarget) {
3115 nsCOMPtr<nsIDocumentViewer> viewer;
3116 mDocShell->GetDocViewer(getter_AddRefs(viewer));
3117 if (viewer) {
3118 RefPtr<nsPresContext> presContext = viewer->GetPresContext();
3120 nsEventStatus status = nsEventStatus_eIgnore;
3121 WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
3123 nsresult rv = EventDispatcher::Dispatch(eventTarget, presContext, &event,
3124 nullptr, &status);
3125 if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault)
3126 return true;
3127 // else fall through and return false
3131 return false;
3132 } // ExecuteCloseHandler
3134 void AppWindow::ConstrainToOpenerScreen(int32_t* aX, int32_t* aY) {
3135 if (mOpenerScreenRect.IsEmpty()) {
3136 *aX = *aY = 0;
3137 return;
3140 int32_t left, top, width, height;
3141 // Constrain initial positions to the same screen as opener
3142 nsCOMPtr<nsIScreenManager> screenmgr =
3143 do_GetService("@mozilla.org/gfx/screenmanager;1");
3144 if (screenmgr) {
3145 nsCOMPtr<nsIScreen> screen = screenmgr->ScreenForRect(mOpenerScreenRect);
3146 if (screen) {
3147 screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
3148 if (*aX < left || *aX > left + width) {
3149 *aX = left;
3151 if (*aY < top || *aY > top + height) {
3152 *aY = top;
3158 nsIAppWindow* AppWindow::WidgetListenerDelegate::GetAppWindow() {
3159 return mAppWindow->GetAppWindow();
3162 PresShell* AppWindow::WidgetListenerDelegate::GetPresShell() {
3163 return mAppWindow->GetPresShell();
3166 bool AppWindow::WidgetListenerDelegate::WindowMoved(nsIWidget* aWidget,
3167 int32_t aX, int32_t aY,
3168 ByMoveToRect) {
3169 RefPtr<AppWindow> holder = mAppWindow;
3170 return holder->WindowMoved(aWidget, aX, aY);
3173 bool AppWindow::WidgetListenerDelegate::WindowResized(nsIWidget* aWidget,
3174 int32_t aWidth,
3175 int32_t aHeight) {
3176 RefPtr<AppWindow> holder = mAppWindow;
3177 return holder->WindowResized(aWidget, aWidth, aHeight);
3180 bool AppWindow::WidgetListenerDelegate::RequestWindowClose(nsIWidget* aWidget) {
3181 RefPtr<AppWindow> holder = mAppWindow;
3182 return holder->RequestWindowClose(aWidget);
3185 void AppWindow::WidgetListenerDelegate::SizeModeChanged(nsSizeMode aSizeMode) {
3186 RefPtr<AppWindow> holder = mAppWindow;
3187 holder->SizeModeChanged(aSizeMode);
3190 void AppWindow::WidgetListenerDelegate::MacFullscreenMenubarOverlapChanged(
3191 DesktopCoord aOverlapAmount) {
3192 RefPtr<AppWindow> holder = mAppWindow;
3193 return holder->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
3196 void AppWindow::WidgetListenerDelegate::OcclusionStateChanged(
3197 bool aIsFullyOccluded) {
3198 RefPtr<AppWindow> holder = mAppWindow;
3199 holder->OcclusionStateChanged(aIsFullyOccluded);
3202 void AppWindow::WidgetListenerDelegate::OSToolbarButtonPressed() {
3203 RefPtr<AppWindow> holder = mAppWindow;
3204 holder->OSToolbarButtonPressed();
3207 void AppWindow::WidgetListenerDelegate::WindowActivated() {
3208 RefPtr<AppWindow> holder = mAppWindow;
3209 holder->WindowActivated();
3212 void AppWindow::WidgetListenerDelegate::WindowDeactivated() {
3213 RefPtr<AppWindow> holder = mAppWindow;
3214 holder->WindowDeactivated();
3217 } // namespace mozilla