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