Bug 1700051: part 31.3) Move `mSoftEnd` to `SoftText`. r=smaug
[gecko.git] / xpfe / appshell / AppWindow.cpp
blob7910d152358f0bb40bfe5ea9536be9e2f6746810
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 "mozilla/MathAlgorithms.h"
9 // Local includes
10 #include "AppWindow.h"
11 #include <algorithm>
13 // Helper classes
14 #include "nsPrintfCString.h"
15 #include "nsString.h"
16 #include "nsWidgetsCID.h"
17 #include "nsThreadUtils.h"
18 #include "nsNetCID.h"
19 #include "nsQueryObject.h"
20 #include "mozilla/ProfilerLabels.h"
21 #include "mozilla/Sprintf.h"
23 // Interfaces needed to be included
24 #include "nsGlobalWindowOuter.h"
25 #include "nsIAppShell.h"
26 #include "nsIAppShellService.h"
27 #include "nsIContentViewer.h"
28 #include "mozilla/dom/Document.h"
29 #include "nsPIDOMWindow.h"
30 #include "nsScreen.h"
31 #include "nsIEmbeddingSiteWindow.h"
32 #include "nsIInterfaceRequestor.h"
33 #include "nsIInterfaceRequestorUtils.h"
34 #include "nsIIOService.h"
35 #include "nsIObserverService.h"
36 #include "nsIOpenWindowInfo.h"
37 #include "nsIWindowMediator.h"
38 #include "nsIScreenManager.h"
39 #include "nsIScreen.h"
40 #include "nsIWindowWatcher.h"
41 #include "nsIURI.h"
42 #include "nsAppShellCID.h"
43 #include "nsReadableUtils.h"
44 #include "nsStyleConsts.h"
45 #include "nsPresContext.h"
46 #include "nsContentUtils.h"
47 #include "nsGlobalWindow.h"
48 #include "nsXULTooltipListener.h"
49 #include "nsXULPopupManager.h"
50 #include "nsFocusManager.h"
51 #include "nsContentList.h"
52 #include "nsIDOMWindowUtils.h"
53 #include "nsServiceManagerUtils.h"
55 #include "prenv.h"
56 #include "mozilla/AutoRestore.h"
57 #include "mozilla/Preferences.h"
58 #include "mozilla/PresShell.h"
59 #include "mozilla/Services.h"
60 #include "mozilla/SpinEventLoopUntil.h"
61 #include "mozilla/dom/BarProps.h"
62 #include "mozilla/dom/DOMRect.h"
63 #include "mozilla/dom/Element.h"
64 #include "mozilla/dom/Event.h"
65 #include "mozilla/dom/ScriptSettings.h"
66 #include "mozilla/dom/BrowserHost.h"
67 #include "mozilla/dom/BrowserParent.h"
68 #include "mozilla/dom/LoadURIOptionsBinding.h"
69 #include "mozilla/intl/LocaleService.h"
70 #include "mozilla/EventDispatcher.h"
72 #ifdef XP_WIN
73 # include "mozilla/PreXULSkeletonUI.h"
74 # include "nsIWindowsUIUtils.h"
75 #endif
77 #ifdef MOZ_NEW_XULSTORE
78 # include "mozilla/XULStore.h"
79 #endif
81 #include "mozilla/dom/DocumentL10n.h"
83 #ifdef XP_MACOSX
84 # include "mozilla/widget/NativeMenuSupport.h"
85 # define USE_NATIVE_MENUS
86 #endif
88 #define SIZEMODE_NORMAL u"normal"_ns
89 #define SIZEMODE_MAXIMIZED u"maximized"_ns
90 #define SIZEMODE_MINIMIZED u"minimized"_ns
91 #define SIZEMODE_FULLSCREEN u"fullscreen"_ns
93 #define WINDOWTYPE_ATTRIBUTE u"windowtype"_ns
95 #define PERSIST_ATTRIBUTE u"persist"_ns
96 #define SCREENX_ATTRIBUTE u"screenX"_ns
97 #define SCREENY_ATTRIBUTE u"screenY"_ns
98 #define WIDTH_ATTRIBUTE u"width"_ns
99 #define HEIGHT_ATTRIBUTE u"height"_ns
100 #define MODE_ATTRIBUTE u"sizemode"_ns
101 #define TILED_ATTRIBUTE u"gtktiledwindow"_ns
102 #define ZLEVEL_ATTRIBUTE u"zlevel"_ns
104 #define SIZE_PERSISTENCE_TIMEOUT 500 // msec
106 //*****************************************************************************
107 //*** AppWindow: Object Management
108 //*****************************************************************************
110 namespace mozilla {
112 using dom::AutoNoJSAPI;
113 using dom::BrowserHost;
114 using dom::BrowsingContext;
115 using dom::Document;
116 using dom::Element;
117 using dom::EventTarget;
118 using dom::LoadURIOptions;
120 AppWindow::AppWindow(uint32_t aChromeFlags)
121 : mChromeTreeOwner(nullptr),
122 mContentTreeOwner(nullptr),
123 mPrimaryContentTreeOwner(nullptr),
124 mModalStatus(NS_OK),
125 mFullscreenChangeState(FullscreenChangeState::NotChanging),
126 mContinueModalLoop(false),
127 mDebuting(false),
128 mChromeLoaded(false),
129 mSizingShellFromXUL(false),
130 mShowAfterLoad(false),
131 mIntrinsicallySized(false),
132 mCenterAfterLoad(false),
133 mIsHiddenWindow(false),
134 mLockedUntilChromeLoad(false),
135 mIgnoreXULSize(false),
136 mIgnoreXULPosition(false),
137 mChromeFlagsFrozen(false),
138 mIgnoreXULSizeMode(false),
139 mDestroying(false),
140 mRegistered(false),
141 mPersistentAttributesDirty(0),
142 mPersistentAttributesMask(0),
143 mChromeFlags(aChromeFlags),
144 mSPTimerLock("AppWindow.mSPTimerLock"),
145 mWidgetListenerDelegate(this) {}
147 AppWindow::~AppWindow() {
149 MutexAutoLock lock(mSPTimerLock);
150 if (mSPTimer) mSPTimer->Cancel();
152 Destroy();
155 //*****************************************************************************
156 // AppWindow::nsISupports
157 //*****************************************************************************
159 NS_IMPL_ADDREF(AppWindow)
160 NS_IMPL_RELEASE(AppWindow)
162 NS_INTERFACE_MAP_BEGIN(AppWindow)
163 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAppWindow)
164 NS_INTERFACE_MAP_ENTRY(nsIAppWindow)
165 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
166 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
167 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
168 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
169 NS_INTERFACE_MAP_ENTRY_CONCRETE(AppWindow)
170 NS_INTERFACE_MAP_END
172 nsresult AppWindow::Initialize(nsIAppWindow* aParent, nsIAppWindow* aOpener,
173 int32_t aInitialWidth, int32_t aInitialHeight,
174 bool aIsHiddenWindow,
175 nsWidgetInitData& widgetInitData) {
176 nsresult rv;
177 nsCOMPtr<nsIWidget> parentWidget;
179 mIsHiddenWindow = aIsHiddenWindow;
181 int32_t initialX = 0, initialY = 0;
182 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aOpener));
183 if (base) {
184 int32_t x, y, width, height;
185 rv = base->GetPositionAndSize(&x, &y, &width, &height);
186 if (NS_FAILED(rv)) {
187 mOpenerScreenRect.SetEmpty();
188 } else {
189 double scale;
190 if (NS_SUCCEEDED(base->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
191 mOpenerScreenRect.SetRect(
192 NSToIntRound(x / scale), NSToIntRound(y / scale),
193 NSToIntRound(width / scale), NSToIntRound(height / scale));
194 } else {
195 mOpenerScreenRect.SetRect(x, y, width, height);
197 initialX = mOpenerScreenRect.X();
198 initialY = mOpenerScreenRect.Y();
199 ConstrainToOpenerScreen(&initialX, &initialY);
203 // XXX: need to get the default window size from prefs...
204 // Doesn't come from prefs... will come from CSS/XUL/RDF
205 DesktopIntRect deskRect(initialX, initialY, aInitialWidth, aInitialHeight);
207 // Create top level window
208 if (gfxPlatform::IsHeadless()) {
209 mWindow = nsIWidget::CreateHeadlessWidget();
210 } else {
211 mWindow = nsIWidget::CreateTopLevelWindow();
213 if (!mWindow) {
214 return NS_ERROR_FAILURE;
217 /* This next bit is troublesome. We carry two different versions of a pointer
218 to our parent window. One is the parent window's widget, which is passed
219 to our own widget. The other is a weak reference we keep here to our
220 parent AppWindow. The former is useful to the widget, and we can't
221 trust its treatment of the parent reference because they're platform-
222 specific. The latter is useful to this class.
223 A better implementation would be one in which the parent keeps strong
224 references to its children and closes them before it allows itself
225 to be closed. This would mimic the behaviour of OSes that support
226 top-level child windows in OSes that do not. Later.
228 nsCOMPtr<nsIBaseWindow> parentAsWin(do_QueryInterface(aParent));
229 if (parentAsWin) {
230 parentAsWin->GetMainWidget(getter_AddRefs(parentWidget));
231 mParentWindow = do_GetWeakReference(aParent);
234 mWindow->SetWidgetListener(&mWidgetListenerDelegate);
235 rv = mWindow->Create((nsIWidget*)parentWidget, // Parent nsIWidget
236 nullptr, // Native parent widget
237 deskRect, // Widget dimensions
238 &widgetInitData); // Widget initialization data
239 NS_ENSURE_SUCCESS(rv, rv);
241 LayoutDeviceIntRect r = mWindow->GetClientBounds();
242 // Match the default background color of content. Important on windows
243 // since we no longer use content child widgets.
244 mWindow->SetBackgroundColor(NS_RGB(255, 255, 255));
246 // All Chrome BCs exist within the same BrowsingContextGroup, so we don't need
247 // to pass in the opener window here. The opener is set later, if needed, by
248 // nsWindowWatcher.
249 RefPtr<BrowsingContext> browsingContext =
250 BrowsingContext::CreateIndependent(BrowsingContext::Type::Chrome);
252 // Create web shell
253 mDocShell = nsDocShell::Create(browsingContext);
254 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
256 // Make sure to set the item type on the docshell _before_ calling
257 // InitWindow() so it knows what type it is.
258 NS_ENSURE_SUCCESS(EnsureChromeTreeOwner(), NS_ERROR_FAILURE);
260 mDocShell->SetTreeOwner(mChromeTreeOwner);
262 r.MoveTo(0, 0);
263 NS_ENSURE_SUCCESS(mDocShell->InitWindow(nullptr, mWindow, r.X(), r.Y(),
264 r.Width(), r.Height()),
265 NS_ERROR_FAILURE);
267 // Attach a WebProgress listener.during initialization...
268 mDocShell->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK);
270 mWindow->MaybeDispatchInitialFocusEvent();
272 return rv;
275 //*****************************************************************************
276 // AppWindow::nsIIntefaceRequestor
277 //*****************************************************************************
279 NS_IMETHODIMP AppWindow::GetInterface(const nsIID& aIID, void** aSink) {
280 nsresult rv;
282 NS_ENSURE_ARG_POINTER(aSink);
284 if (aIID.Equals(NS_GET_IID(nsIPrompt))) {
285 rv = EnsurePrompter();
286 if (NS_FAILED(rv)) return rv;
287 return mPrompter->QueryInterface(aIID, aSink);
289 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
290 rv = EnsureAuthPrompter();
291 if (NS_FAILED(rv)) return rv;
292 return mAuthPrompter->QueryInterface(aIID, aSink);
294 if (aIID.Equals(NS_GET_IID(mozIDOMWindowProxy))) {
295 return GetWindowDOMWindow(reinterpret_cast<mozIDOMWindowProxy**>(aSink));
297 if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) {
298 nsCOMPtr<mozIDOMWindowProxy> window = nullptr;
299 rv = GetWindowDOMWindow(getter_AddRefs(window));
300 nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(window);
301 domWindow.forget(aSink);
302 return rv;
304 if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome)) &&
305 NS_SUCCEEDED(EnsureContentTreeOwner()) &&
306 NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink)))
307 return NS_OK;
309 if (aIID.Equals(NS_GET_IID(nsIEmbeddingSiteWindow)) &&
310 NS_SUCCEEDED(EnsureContentTreeOwner()) &&
311 NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink)))
312 return NS_OK;
314 return QueryInterface(aIID, aSink);
317 //*****************************************************************************
318 // AppWindow::nsIAppWindow
319 //*****************************************************************************
321 NS_IMETHODIMP AppWindow::GetDocShell(nsIDocShell** aDocShell) {
322 NS_ENSURE_ARG_POINTER(aDocShell);
324 *aDocShell = mDocShell;
325 NS_IF_ADDREF(*aDocShell);
326 return NS_OK;
329 NS_IMETHODIMP AppWindow::GetZLevel(uint32_t* outLevel) {
330 nsCOMPtr<nsIWindowMediator> mediator(
331 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
332 if (mediator)
333 mediator->GetZLevel(this, outLevel);
334 else
335 *outLevel = normalZ;
336 return NS_OK;
339 NS_IMETHODIMP AppWindow::SetZLevel(uint32_t aLevel) {
340 nsCOMPtr<nsIWindowMediator> mediator(
341 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
342 if (!mediator) return NS_ERROR_FAILURE;
344 uint32_t zLevel;
345 mediator->GetZLevel(this, &zLevel);
346 if (zLevel == aLevel) return NS_OK;
348 /* refuse to raise a maximized window above the normal browser level,
349 for fear it could hide newly opened browser windows */
350 if (aLevel > nsIAppWindow::normalZ && mWindow) {
351 nsSizeMode sizeMode = mWindow->SizeMode();
352 if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) {
353 return NS_ERROR_FAILURE;
357 // do it
358 mediator->SetZLevel(this, aLevel);
359 PersistentAttributesDirty(PAD_MISC);
360 SavePersistentAttributes();
362 nsCOMPtr<nsIContentViewer> cv;
363 mDocShell->GetContentViewer(getter_AddRefs(cv));
364 if (cv) {
365 RefPtr<dom::Document> doc = cv->GetDocument();
366 if (doc) {
367 ErrorResult rv;
368 RefPtr<dom::Event> event =
369 doc->CreateEvent(u"Events"_ns, dom::CallerType::System, rv);
370 if (event) {
371 event->InitEvent(u"windowZLevel"_ns, true, false);
373 event->SetTrusted(true);
375 doc->DispatchEvent(*event);
379 return NS_OK;
382 NS_IMETHODIMP AppWindow::GetChromeFlags(uint32_t* aChromeFlags) {
383 NS_ENSURE_ARG_POINTER(aChromeFlags);
384 *aChromeFlags = mChromeFlags;
385 return NS_OK;
388 NS_IMETHODIMP AppWindow::SetChromeFlags(uint32_t aChromeFlags) {
389 NS_ASSERTION(!mChromeFlagsFrozen,
390 "SetChromeFlags() after AssumeChromeFlagsAreFrozen()!");
392 mChromeFlags = aChromeFlags;
393 if (mChromeLoaded) {
394 ApplyChromeFlags();
396 return NS_OK;
399 NS_IMETHODIMP AppWindow::AssumeChromeFlagsAreFrozen() {
400 mChromeFlagsFrozen = true;
401 return NS_OK;
404 NS_IMETHODIMP AppWindow::SetIntrinsicallySized(bool aIntrinsicallySized) {
405 mIntrinsicallySized = aIntrinsicallySized;
406 return NS_OK;
409 NS_IMETHODIMP AppWindow::GetIntrinsicallySized(bool* aIntrinsicallySized) {
410 NS_ENSURE_ARG_POINTER(aIntrinsicallySized);
412 *aIntrinsicallySized = mIntrinsicallySized;
413 return NS_OK;
416 NS_IMETHODIMP AppWindow::GetPrimaryContentShell(
417 nsIDocShellTreeItem** aDocShellTreeItem) {
418 NS_ENSURE_ARG_POINTER(aDocShellTreeItem);
419 NS_IF_ADDREF(*aDocShellTreeItem = mPrimaryContentShell);
420 return NS_OK;
423 NS_IMETHODIMP
424 AppWindow::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
425 if (aPrimary) {
426 mPrimaryBrowserParent = aTab;
427 mPrimaryContentShell = nullptr;
428 } else if (mPrimaryBrowserParent == aTab) {
429 mPrimaryBrowserParent = nullptr;
432 return NS_OK;
435 NS_IMETHODIMP
436 AppWindow::RemoteTabRemoved(nsIRemoteTab* aTab) {
437 if (aTab == mPrimaryBrowserParent) {
438 mPrimaryBrowserParent = nullptr;
441 return NS_OK;
444 NS_IMETHODIMP
445 AppWindow::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
446 nsCOMPtr<nsIRemoteTab> tab = mPrimaryBrowserParent;
447 tab.forget(aTab);
448 return NS_OK;
451 static LayoutDeviceIntSize GetOuterToInnerSizeDifference(nsIWidget* aWindow) {
452 if (!aWindow) {
453 return LayoutDeviceIntSize();
455 LayoutDeviceIntSize baseSize(200, 200);
456 LayoutDeviceIntSize windowSize = aWindow->ClientToWindowSize(baseSize);
457 return windowSize - baseSize;
460 static CSSIntSize GetOuterToInnerSizeDifferenceInCSSPixels(nsIWidget* aWindow) {
461 if (!aWindow) {
462 return {};
464 LayoutDeviceIntSize devPixelSize = GetOuterToInnerSizeDifference(aWindow);
465 return RoundedToInt(devPixelSize / aWindow->GetDefaultScale());
468 NS_IMETHODIMP
469 AppWindow::GetOuterToInnerHeightDifferenceInCSSPixels(uint32_t* aResult) {
470 *aResult = GetOuterToInnerSizeDifferenceInCSSPixels(mWindow).height;
471 return NS_OK;
474 NS_IMETHODIMP
475 AppWindow::GetOuterToInnerWidthDifferenceInCSSPixels(uint32_t* aResult) {
476 *aResult = GetOuterToInnerSizeDifferenceInCSSPixels(mWindow).width;
477 return NS_OK;
480 nsTArray<RefPtr<mozilla::LiveResizeListener>>
481 AppWindow::GetLiveResizeListeners() {
482 nsTArray<RefPtr<mozilla::LiveResizeListener>> listeners;
483 if (mPrimaryBrowserParent) {
484 BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
485 listeners.AppendElement(host->GetActor());
487 return listeners;
490 NS_IMETHODIMP AppWindow::AddChildWindow(nsIAppWindow* aChild) {
491 // we're not really keeping track of this right now
492 return NS_OK;
495 NS_IMETHODIMP AppWindow::RemoveChildWindow(nsIAppWindow* aChild) {
496 // we're not really keeping track of this right now
497 return NS_OK;
500 NS_IMETHODIMP AppWindow::ShowModal() {
501 AUTO_PROFILER_LABEL("AppWindow::ShowModal", OTHER);
503 // Store locally so it doesn't die on us
504 nsCOMPtr<nsIWidget> window = mWindow;
505 nsCOMPtr<nsIAppWindow> tempRef = this;
507 window->SetModal(true);
508 mContinueModalLoop = true;
509 EnableParent(false);
512 AutoNoJSAPI nojsapi;
513 SpinEventLoopUntil([&]() { return !mContinueModalLoop; });
516 mContinueModalLoop = false;
517 window->SetModal(false);
518 /* Note there's no EnableParent(true) here to match the false one
519 above. That's done in ExitModalLoop. It's important that the parent
520 be re-enabled before this window is made invisible; to do otherwise
521 causes bizarre z-ordering problems. At this point, the window is
522 already invisible.
523 No known current implementation of Enable would have a problem with
524 re-enabling the parent twice, so we could do it again here without
525 breaking any current implementation. But that's unnecessary if the
526 modal loop is always exited using ExitModalLoop (the other way would be
527 to change the protected member variable directly.)
530 return mModalStatus;
533 //*****************************************************************************
534 // AppWindow::nsIBaseWindow
535 //*****************************************************************************
537 NS_IMETHODIMP AppWindow::InitWindow(nativeWindow aParentNativeWindow,
538 nsIWidget* parentWidget, int32_t x,
539 int32_t y, int32_t cx, int32_t cy) {
540 // XXX First Check In
541 NS_ASSERTION(false, "Not Yet Implemented");
542 return NS_OK;
545 NS_IMETHODIMP AppWindow::Destroy() {
546 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
548 if (mDocShell) {
549 mDocShell->RemoveProgressListener(this);
553 MutexAutoLock lock(mSPTimerLock);
554 if (mSPTimer) {
555 mSPTimer->Cancel();
556 SavePersistentAttributes();
557 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));
575 nsCOMPtr<nsIAppWindow> parentWindow(do_QueryReferent(mParentWindow));
576 if (parentWindow) parentWindow->RemoveChildWindow(this);
578 // Remove modality (if any) and hide while destroying. More than
579 // a convenience, the hide prevents user interaction with the partially
580 // destroyed window. This is especially necessary when the eldest window
581 // in a stack of modal windows is destroyed first. It happens.
582 ExitModalLoop(NS_OK);
583 // XXX: Skip unmapping the window on Linux due to GLX hangs on the compositor
584 // thread with NVIDIA driver 310.32. We don't need to worry about user
585 // interactions with destroyed windows on X11 either.
586 #ifndef MOZ_WIDGET_GTK
587 if (mWindow) mWindow->Show(false);
588 #endif
590 #if defined(XP_WIN)
591 // We need to explicitly set the focus on Windows, but
592 // only if the parent is visible.
593 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
594 if (parent) {
595 nsCOMPtr<nsIWidget> parentWidget;
596 parent->GetMainWidget(getter_AddRefs(parentWidget));
598 if (parentWidget && parentWidget->IsVisible()) {
599 bool isParentHiddenWindow = false;
601 if (appShell) {
602 bool hasHiddenWindow = false;
603 appShell->GetHasHiddenWindow(&hasHiddenWindow);
604 if (hasHiddenWindow) {
605 nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
606 nsCOMPtr<nsIAppWindow> hiddenWindow;
607 appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
608 if (hiddenWindow) {
609 baseHiddenWindow = do_GetInterface(hiddenWindow);
610 isParentHiddenWindow = (baseHiddenWindow == parent);
615 // somebody screwed up somewhere. hiddenwindow shouldn't be anybody's
616 // parent. still, when it happens, skip activating it.
617 if (!isParentHiddenWindow) {
618 parentWidget->PlaceBehind(eZPlacementTop, 0, true);
622 #endif
624 RemoveTooltipSupport();
626 mDOMWindow = nullptr;
627 if (mDocShell) {
628 RefPtr<BrowsingContext> bc(mDocShell->GetBrowsingContext());
629 mDocShell->Destroy();
630 bc->Detach();
631 mDocShell = nullptr; // this can cause reentrancy of this function
634 mPrimaryContentShell = nullptr;
636 if (mContentTreeOwner) {
637 mContentTreeOwner->AppWindow(nullptr);
638 NS_RELEASE(mContentTreeOwner);
640 if (mPrimaryContentTreeOwner) {
641 mPrimaryContentTreeOwner->AppWindow(nullptr);
642 NS_RELEASE(mPrimaryContentTreeOwner);
644 if (mChromeTreeOwner) {
645 mChromeTreeOwner->AppWindow(nullptr);
646 NS_RELEASE(mChromeTreeOwner);
648 if (mWindow) {
649 mWindow->SetWidgetListener(nullptr); // nsWebShellWindow hackery
650 mWindow->Destroy();
651 mWindow = nullptr;
654 if (!mIsHiddenWindow && mRegistered) {
655 /* Inform appstartup we've destroyed this window and it could
656 quit now if it wanted. This must happen at least after mDocShell
657 is destroyed, because onunload handlers fire then, and those being
658 script, anything could happen. A new window could open, even.
659 See bug 130719. */
660 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
661 NS_ASSERTION(obssvc, "Couldn't get observer service?");
663 if (obssvc)
664 obssvc->NotifyObservers(nullptr, "xul-window-destroyed", nullptr);
667 return NS_OK;
670 NS_IMETHODIMP AppWindow::GetDevicePixelsPerDesktopPixel(double* aScale) {
671 *aScale = mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0;
672 return NS_OK;
675 NS_IMETHODIMP AppWindow::GetUnscaledDevicePixelsPerCSSPixel(double* aScale) {
676 *aScale = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
677 return NS_OK;
680 NS_IMETHODIMP AppWindow::SetPositionDesktopPix(int32_t aX, int32_t aY) {
681 mWindow->Move(aX, aY);
682 if (mSizingShellFromXUL) {
683 // If we're invoked for sizing from XUL, we want to neither ignore anything
684 // nor persist anything, since it's already the value in XUL.
685 return NS_OK;
687 if (!mChromeLoaded) {
688 // If we're called before the chrome is loaded someone obviously wants this
689 // window at this position. We don't persist this one-time position.
690 mIgnoreXULPosition = true;
691 return NS_OK;
693 PersistentAttributesDirty(PAD_POSITION);
694 SavePersistentAttributes();
695 return NS_OK;
698 // The parameters here are device pixels; do the best we can to convert to
699 // desktop px, using the window's current scale factor (if available).
700 NS_IMETHODIMP AppWindow::SetPosition(int32_t aX, int32_t aY) {
701 // Don't reset the window's size mode here - platforms that don't want to move
702 // maximized windows should reset it in their respective Move implementation.
703 DesktopToLayoutDeviceScale currScale = mWindow->GetDesktopToDeviceScale();
704 DesktopPoint pos = LayoutDeviceIntPoint(aX, aY) / currScale;
705 return SetPositionDesktopPix(pos.x, pos.y);
708 NS_IMETHODIMP AppWindow::GetPosition(int32_t* aX, int32_t* aY) {
709 return GetPositionAndSize(aX, aY, nullptr, nullptr);
712 NS_IMETHODIMP AppWindow::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) {
713 /* any attempt to set the window's size or position overrides the window's
714 zoom state. this is important when these two states are competing while
715 the window is being opened. but it should probably just always be so. */
716 mWindow->SetSizeMode(nsSizeMode_Normal);
718 mIntrinsicallySized = false;
720 DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
721 DesktopSize size = LayoutDeviceIntSize(aCX, aCY) / scale;
722 mWindow->Resize(size.width, size.height, aRepaint);
723 if (mSizingShellFromXUL) {
724 // If we're invoked for sizing from XUL, we want to neither ignore anything
725 // nor persist anything, since it's already the value in XUL.
726 return NS_OK;
728 if (!mChromeLoaded) {
729 // If we're called before the chrome is loaded someone obviously wants this
730 // window at this size & in the normal size mode (since it is the only mode
731 // in which setting dimensions makes sense). We don't persist this one-time
732 // size.
733 mIgnoreXULSize = true;
734 mIgnoreXULSizeMode = true;
735 return NS_OK;
737 PersistentAttributesDirty(PAD_SIZE);
738 SavePersistentAttributes();
739 return NS_OK;
742 NS_IMETHODIMP AppWindow::GetSize(int32_t* aCX, int32_t* aCY) {
743 return GetPositionAndSize(nullptr, nullptr, aCX, aCY);
746 NS_IMETHODIMP AppWindow::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
747 int32_t aCY, uint32_t aFlags) {
748 /* any attempt to set the window's size or position overrides the window's
749 zoom state. this is important when these two states are competing while
750 the window is being opened. but it should probably just always be so. */
751 mWindow->SetSizeMode(nsSizeMode_Normal);
753 mIntrinsicallySized = false;
755 DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
756 DesktopRect rect = LayoutDeviceIntRect(aX, aY, aCX, aCY) / scale;
757 mWindow->Resize(rect.X(), rect.Y(), rect.Width(), rect.Height(),
758 !!(aFlags & nsIBaseWindow::eRepaint));
759 if (mSizingShellFromXUL) {
760 // If we're invoked for sizing from XUL, we want to neither ignore anything
761 // nor persist anything, since it's already the value in XUL.
762 return NS_OK;
764 if (!mChromeLoaded) {
765 // If we're called before the chrome is loaded someone obviously wants this
766 // window at this size and position. We don't persist this one-time setting.
767 mIgnoreXULPosition = true;
768 mIgnoreXULSize = true;
769 mIgnoreXULSizeMode = true;
770 return NS_OK;
772 PersistentAttributesDirty(PAD_POSITION | PAD_SIZE);
773 SavePersistentAttributes();
774 return NS_OK;
777 NS_IMETHODIMP AppWindow::GetPositionAndSize(int32_t* x, int32_t* y, int32_t* cx,
778 int32_t* cy) {
779 if (!mWindow) return NS_ERROR_FAILURE;
781 LayoutDeviceIntRect rect = mWindow->GetScreenBounds();
783 if (x) *x = rect.X();
784 if (y) *y = rect.Y();
785 if (cx) *cx = rect.Width();
786 if (cy) *cy = rect.Height();
788 return NS_OK;
791 NS_IMETHODIMP AppWindow::Center(nsIAppWindow* aRelative, bool aScreen,
792 bool aAlert) {
793 int32_t left, top, width, height, ourWidth, ourHeight;
794 bool screenCoordinates = false, windowCoordinates = false;
795 nsresult result;
797 if (!mChromeLoaded) {
798 // note we lose the parameters. at time of writing, this isn't a problem.
799 mCenterAfterLoad = true;
800 return NS_OK;
803 if (!aScreen && !aRelative) return NS_ERROR_INVALID_ARG;
805 nsCOMPtr<nsIScreenManager> screenmgr =
806 do_GetService("@mozilla.org/gfx/screenmanager;1", &result);
807 if (NS_FAILED(result)) return result;
809 nsCOMPtr<nsIScreen> screen;
811 if (aRelative) {
812 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aRelative, &result));
813 if (base) {
814 // get window rect
815 result = base->GetPositionAndSize(&left, &top, &width, &height);
816 if (NS_SUCCEEDED(result)) {
817 double scale;
818 if (NS_SUCCEEDED(base->GetDevicePixelsPerDesktopPixel(&scale))) {
819 left = NSToIntRound(left / scale);
820 top = NSToIntRound(top / scale);
821 width = NSToIntRound(width / scale);
822 height = NSToIntRound(height / scale);
824 // if centering on screen, convert that to the corresponding screen
825 if (aScreen)
826 screenmgr->ScreenForRect(left, top, width, height,
827 getter_AddRefs(screen));
828 else
829 windowCoordinates = true;
830 } else {
831 // something's wrong with the reference window.
832 // fall back to the primary screen
833 aRelative = 0;
834 aScreen = true;
838 if (!aRelative) {
839 if (!mOpenerScreenRect.IsEmpty()) {
840 // FIXME - check if these are device or display pixels
841 screenmgr->ScreenForRect(mOpenerScreenRect.X(), mOpenerScreenRect.Y(),
842 mOpenerScreenRect.Width(),
843 mOpenerScreenRect.Height(),
844 getter_AddRefs(screen));
845 } else {
846 screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
850 if (aScreen && screen) {
851 screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
852 screenCoordinates = true;
855 if (screenCoordinates || windowCoordinates) {
856 NS_ASSERTION(mWindow, "what, no window?");
857 double scale = mWindow->GetDesktopToDeviceScale().scale;
858 GetSize(&ourWidth, &ourHeight);
859 int32_t scaledWidth, scaledHeight;
860 scaledWidth = NSToIntRound(ourWidth / scale);
861 scaledHeight = NSToIntRound(ourHeight / scale);
862 left += (width - scaledWidth) / 2;
863 top += (height - scaledHeight) / (aAlert ? 3 : 2);
864 if (windowCoordinates) {
865 mWindow->ConstrainPosition(false, &left, &top);
867 SetPosition(left * scale, top * scale);
869 // If moving the window caused it to change size,
870 // re-do the centering.
871 int32_t newWidth, newHeight;
872 GetSize(&newWidth, &newHeight);
873 if (newWidth != ourWidth || newHeight != ourHeight) {
874 return Center(aRelative, aScreen, aAlert);
876 return NS_OK;
879 return NS_ERROR_FAILURE;
882 NS_IMETHODIMP AppWindow::Repaint(bool aForce) {
883 // XXX First Check In
884 NS_ASSERTION(false, "Not Yet Implemented");
885 return NS_OK;
888 NS_IMETHODIMP AppWindow::GetParentWidget(nsIWidget** aParentWidget) {
889 NS_ENSURE_ARG_POINTER(aParentWidget);
890 NS_ENSURE_STATE(mWindow);
892 NS_IF_ADDREF(*aParentWidget = mWindow->GetParent());
893 return NS_OK;
896 NS_IMETHODIMP AppWindow::SetParentWidget(nsIWidget* aParentWidget) {
897 // XXX First Check In
898 NS_ASSERTION(false, "Not Yet Implemented");
899 return NS_OK;
902 NS_IMETHODIMP AppWindow::GetParentNativeWindow(
903 nativeWindow* aParentNativeWindow) {
904 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
906 nsCOMPtr<nsIWidget> parentWidget;
907 NS_ENSURE_SUCCESS(GetParentWidget(getter_AddRefs(parentWidget)),
908 NS_ERROR_FAILURE);
910 if (parentWidget) {
911 *aParentNativeWindow = parentWidget->GetNativeData(NS_NATIVE_WIDGET);
914 return NS_OK;
917 NS_IMETHODIMP AppWindow::SetParentNativeWindow(
918 nativeWindow aParentNativeWindow) {
919 // XXX First Check In
920 NS_ASSERTION(false, "Not Yet Implemented");
921 return NS_OK;
924 NS_IMETHODIMP AppWindow::GetNativeHandle(nsAString& aNativeHandle) {
925 nsCOMPtr<nsIWidget> mainWidget;
926 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(mainWidget)),
927 NS_ERROR_FAILURE);
929 if (mainWidget) {
930 nativeWindow nativeWindowPtr = mainWidget->GetNativeData(NS_NATIVE_WINDOW);
931 /* the nativeWindow pointer is converted to and exposed as a string. This
932 is a more reliable way not to lose information (as opposed to JS
933 |Number| for instance) */
934 aNativeHandle =
935 NS_ConvertASCIItoUTF16(nsPrintfCString("0x%p", nativeWindowPtr));
938 return NS_OK;
941 NS_IMETHODIMP AppWindow::GetVisibility(bool* aVisibility) {
942 NS_ENSURE_ARG_POINTER(aVisibility);
944 // Always claim to be visible for now. See bug
945 // https://bugzilla.mozilla.org/show_bug.cgi?id=306245.
947 *aVisibility = true;
949 return NS_OK;
952 NS_IMETHODIMP AppWindow::SetVisibility(bool aVisibility) {
953 if (!mChromeLoaded) {
954 mShowAfterLoad = aVisibility;
955 return NS_OK;
958 if (mDebuting) {
959 return NS_OK;
961 mDebuting = true; // (Show / Focus is recursive)
963 // XXXTAB Do we really need to show docshell and the window? Isn't
964 // the window good enough?
965 mDocShell->SetVisibility(aVisibility);
966 // Store locally so it doesn't die on us. 'Show' can result in the window
967 // being closed with AppWindow::Destroy being called. That would set
968 // mWindow to null and posibly destroy the nsIWidget while its Show method
969 // is on the stack. We need to keep it alive until Show finishes.
970 nsCOMPtr<nsIWidget> window = mWindow;
971 window->Show(aVisibility);
973 nsCOMPtr<nsIWindowMediator> windowMediator(
974 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
975 if (windowMediator)
976 windowMediator->UpdateWindowTimeStamp(static_cast<nsIAppWindow*>(this));
978 // notify observers so that we can hide the splash screen if possible
979 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
980 NS_ASSERTION(obssvc, "Couldn't get observer service.");
981 if (obssvc) {
982 obssvc->NotifyObservers(static_cast<nsIAppWindow*>(this),
983 "xul-window-visible", nullptr);
986 mDebuting = false;
987 return NS_OK;
990 NS_IMETHODIMP AppWindow::GetEnabled(bool* aEnabled) {
991 NS_ENSURE_ARG_POINTER(aEnabled);
993 if (mWindow) {
994 *aEnabled = mWindow->IsEnabled();
995 return NS_OK;
998 *aEnabled = true; // better guess than most
999 return NS_ERROR_FAILURE;
1002 NS_IMETHODIMP AppWindow::SetEnabled(bool aEnable) {
1003 if (mWindow) {
1004 mWindow->Enable(aEnable);
1005 return NS_OK;
1007 return NS_ERROR_FAILURE;
1010 NS_IMETHODIMP AppWindow::GetMainWidget(nsIWidget** aMainWidget) {
1011 NS_ENSURE_ARG_POINTER(aMainWidget);
1013 *aMainWidget = mWindow;
1014 NS_IF_ADDREF(*aMainWidget);
1015 return NS_OK;
1018 NS_IMETHODIMP AppWindow::SetFocus() {
1019 // XXX First Check In
1020 NS_ASSERTION(false, "Not Yet Implemented");
1021 return NS_OK;
1024 NS_IMETHODIMP AppWindow::GetTitle(nsAString& aTitle) {
1025 aTitle = mTitle;
1026 return NS_OK;
1029 NS_IMETHODIMP AppWindow::SetTitle(const nsAString& aTitle) {
1030 NS_ENSURE_STATE(mWindow);
1031 mTitle.Assign(aTitle);
1032 mTitle.StripCRLF();
1033 NS_ENSURE_SUCCESS(mWindow->SetTitle(mTitle), NS_ERROR_FAILURE);
1034 return NS_OK;
1037 //*****************************************************************************
1038 // AppWindow: Helpers
1039 //*****************************************************************************
1041 NS_IMETHODIMP AppWindow::EnsureChromeTreeOwner() {
1042 if (mChromeTreeOwner) return NS_OK;
1044 mChromeTreeOwner = new nsChromeTreeOwner();
1045 NS_ADDREF(mChromeTreeOwner);
1046 mChromeTreeOwner->AppWindow(this);
1048 return NS_OK;
1051 NS_IMETHODIMP AppWindow::EnsureContentTreeOwner() {
1052 if (mContentTreeOwner) return NS_OK;
1054 mContentTreeOwner = new nsContentTreeOwner(false);
1055 NS_ADDREF(mContentTreeOwner);
1056 mContentTreeOwner->AppWindow(this);
1058 return NS_OK;
1061 NS_IMETHODIMP AppWindow::EnsurePrimaryContentTreeOwner() {
1062 if (mPrimaryContentTreeOwner) return NS_OK;
1064 mPrimaryContentTreeOwner = new nsContentTreeOwner(true);
1065 NS_ADDREF(mPrimaryContentTreeOwner);
1066 mPrimaryContentTreeOwner->AppWindow(this);
1068 return NS_OK;
1071 NS_IMETHODIMP AppWindow::EnsurePrompter() {
1072 if (mPrompter) return NS_OK;
1074 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1075 nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow));
1076 if (NS_SUCCEEDED(rv)) {
1077 nsCOMPtr<nsIWindowWatcher> wwatch =
1078 do_GetService(NS_WINDOWWATCHER_CONTRACTID);
1079 if (wwatch) wwatch->GetNewPrompter(ourWindow, getter_AddRefs(mPrompter));
1081 return mPrompter ? NS_OK : NS_ERROR_FAILURE;
1084 NS_IMETHODIMP AppWindow::EnsureAuthPrompter() {
1085 if (mAuthPrompter) return NS_OK;
1087 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1088 nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow));
1089 if (NS_SUCCEEDED(rv)) {
1090 nsCOMPtr<nsIWindowWatcher> wwatch(
1091 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
1092 if (wwatch)
1093 wwatch->GetNewAuthPrompter(ourWindow, getter_AddRefs(mAuthPrompter));
1095 return mAuthPrompter ? NS_OK : NS_ERROR_FAILURE;
1098 NS_IMETHODIMP AppWindow::GetAvailScreenSize(int32_t* aAvailWidth,
1099 int32_t* aAvailHeight) {
1100 nsCOMPtr<mozIDOMWindowProxy> domWindow;
1101 GetWindowDOMWindow(getter_AddRefs(domWindow));
1102 NS_ENSURE_STATE(domWindow);
1104 auto* window = nsGlobalWindowOuter::Cast(domWindow);
1106 RefPtr<nsScreen> screen = window->GetScreen();
1107 NS_ENSURE_STATE(screen);
1109 ErrorResult rv;
1110 *aAvailWidth = screen->GetAvailWidth(rv);
1111 if (NS_WARN_IF(rv.Failed())) {
1112 return rv.StealNSResult();
1115 *aAvailHeight = screen->GetAvailHeight(rv);
1116 if (NS_WARN_IF(rv.Failed())) {
1117 return rv.StealNSResult();
1120 return NS_OK;
1123 // Rounds window size to 1000x1000, or, if there isn't enough available
1124 // screen space, to a multiple of 200x100.
1125 NS_IMETHODIMP AppWindow::ForceRoundedDimensions() {
1126 if (mIsHiddenWindow) {
1127 return NS_OK;
1130 int32_t availWidthCSS = 0;
1131 int32_t availHeightCSS = 0;
1132 int32_t contentWidthCSS = 0;
1133 int32_t contentHeightCSS = 0;
1134 int32_t windowWidthCSS = 0;
1135 int32_t windowHeightCSS = 0;
1136 double devicePerCSSPixels = 1.0;
1138 GetUnscaledDevicePixelsPerCSSPixel(&devicePerCSSPixels);
1140 GetAvailScreenSize(&availWidthCSS, &availHeightCSS);
1142 // To get correct chrome size, we have to resize the window to a proper
1143 // size first. So, here, we size it to its available size.
1144 SetSpecifiedSize(availWidthCSS, availHeightCSS);
1146 // Get the current window size for calculating chrome UI size.
1147 GetSize(&windowWidthCSS, &windowHeightCSS); // device pixels
1148 windowWidthCSS = NSToIntRound(windowWidthCSS / devicePerCSSPixels);
1149 windowHeightCSS = NSToIntRound(windowHeightCSS / devicePerCSSPixels);
1151 // Get the content size for calculating chrome UI size.
1152 GetPrimaryContentSize(&contentWidthCSS, &contentHeightCSS);
1154 // Calculate the chrome UI size.
1155 int32_t chromeWidth = 0, chromeHeight = 0;
1156 chromeWidth = windowWidthCSS - contentWidthCSS;
1157 chromeHeight = windowHeightCSS - contentHeightCSS;
1159 int32_t targetContentWidth = 0, targetContentHeight = 0;
1161 // Here, we use the available screen dimensions as the input dimensions to
1162 // force the window to be rounded as the maximum available content size.
1163 nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
1164 chromeWidth, chromeHeight, availWidthCSS, availHeightCSS, availWidthCSS,
1165 availHeightCSS,
1166 false, // aSetOuterWidth
1167 false, // aSetOuterHeight
1168 &targetContentWidth, &targetContentHeight);
1170 targetContentWidth = NSToIntRound(targetContentWidth * devicePerCSSPixels);
1171 targetContentHeight = NSToIntRound(targetContentHeight * devicePerCSSPixels);
1173 SetPrimaryContentSize(targetContentWidth, targetContentHeight);
1175 return NS_OK;
1178 void AppWindow::OnChromeLoaded() {
1179 nsresult rv = EnsureContentTreeOwner();
1181 if (NS_SUCCEEDED(rv)) {
1182 mChromeLoaded = true;
1183 ApplyChromeFlags();
1184 SyncAttributesToWidget();
1185 if (mWindow) {
1186 SizeShell();
1187 if (mShowAfterLoad) {
1188 SetVisibility(true);
1190 AddTooltipSupport();
1192 // At this point the window may have been closed already during Show() or
1193 // SyncAttributesToWidget(), so AppWindow::Destroy may already have been
1194 // called. Take care!
1196 mPersistentAttributesMask |= PAD_POSITION | PAD_SIZE | PAD_MISC;
1199 bool AppWindow::NeedsTooltipListener() {
1200 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1201 if (!docShellElement || docShellElement->IsXULElement()) {
1202 // Tooltips in XUL are handled by each element.
1203 return false;
1205 // All other non-XUL document types need a tooltip listener.
1206 return true;
1209 void AppWindow::AddTooltipSupport() {
1210 if (!NeedsTooltipListener()) {
1211 return;
1213 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
1214 if (!listener) {
1215 return;
1218 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1219 MOZ_ASSERT(docShellElement);
1220 listener->AddTooltipSupport(docShellElement);
1223 void AppWindow::RemoveTooltipSupport() {
1224 if (!NeedsTooltipListener()) {
1225 return;
1227 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
1228 if (!listener) {
1229 return;
1232 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1233 MOZ_ASSERT(docShellElement);
1234 listener->RemoveTooltipSupport(docShellElement);
1237 // If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes
1238 // to fit to the screen when staggering windows; if they're negative,
1239 // we use the window's current size instead.
1240 bool AppWindow::LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight) {
1241 bool gotPosition = false;
1243 // if we're the hidden window, don't try to validate our size/position. We're
1244 // special.
1245 if (mIsHiddenWindow) return false;
1247 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1248 NS_ENSURE_TRUE(windowElement, false);
1250 int32_t currX = 0;
1251 int32_t currY = 0;
1252 int32_t currWidth = 0;
1253 int32_t currHeight = 0;
1254 nsresult errorCode;
1255 int32_t temp;
1257 GetPositionAndSize(&currX, &currY, &currWidth, &currHeight);
1259 // Convert to global display pixels for consistent window management across
1260 // screens with diverse resolutions
1261 double devToDesktopScale = 1.0 / mWindow->GetDesktopToDeviceScale().scale;
1262 currX = NSToIntRound(currX * devToDesktopScale);
1263 currY = NSToIntRound(currY * devToDesktopScale);
1265 // For size, use specified value if > 0, else current value
1266 double devToCSSScale = 1.0 / mWindow->GetDefaultScale().scale;
1267 int32_t cssWidth =
1268 aSpecWidth > 0 ? aSpecWidth : NSToIntRound(currWidth * devToCSSScale);
1269 int32_t cssHeight =
1270 aSpecHeight > 0 ? aSpecHeight : NSToIntRound(currHeight * devToCSSScale);
1272 // Obtain the position information from the <xul:window> element.
1273 int32_t specX = currX;
1274 int32_t specY = currY;
1275 nsAutoString posString;
1277 windowElement->GetAttribute(SCREENX_ATTRIBUTE, posString);
1278 temp = posString.ToInteger(&errorCode);
1279 if (NS_SUCCEEDED(errorCode)) {
1280 specX = temp;
1281 gotPosition = true;
1283 windowElement->GetAttribute(SCREENY_ATTRIBUTE, posString);
1284 temp = posString.ToInteger(&errorCode);
1285 if (NS_SUCCEEDED(errorCode)) {
1286 specY = temp;
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 int32_t parentX, parentY;
1295 if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
1296 double scale;
1297 if (NS_SUCCEEDED(parent->GetDevicePixelsPerDesktopPixel(&scale))) {
1298 parentX = NSToIntRound(parentX / scale);
1299 parentY = NSToIntRound(parentY / scale);
1301 specX += parentX;
1302 specY += parentY;
1304 } else {
1305 StaggerPosition(specX, specY, cssWidth, cssHeight);
1308 mWindow->ConstrainPosition(false, &specX, &specY);
1309 if (specX != currX || specY != currY) {
1310 SetPositionDesktopPix(specX, specY);
1313 return gotPosition;
1316 static Maybe<int32_t> ReadIntAttribute(const Element& aElement, nsAtom* aAtom) {
1317 nsAutoString attrString;
1318 if (!aElement.GetAttr(kNameSpaceID_None, aAtom, attrString)) {
1319 return Nothing();
1322 nsresult res = NS_OK;
1323 int32_t ret = attrString.ToInteger(&res);
1324 return NS_SUCCEEDED(res) ? Some(ret) : Nothing();
1327 static Maybe<int32_t> ReadSize(const Element& aElement, nsAtom* aAttr,
1328 nsAtom* aMinAttr, nsAtom* aMaxAttr) {
1329 Maybe<int32_t> attr = ReadIntAttribute(aElement, aAttr);
1330 if (!attr) {
1331 return Nothing();
1334 int32_t min =
1335 std::max(100, ReadIntAttribute(aElement, aMinAttr).valueOr(100));
1336 int32_t max = ReadIntAttribute(aElement, aMaxAttr)
1337 .valueOr(std::numeric_limits<int32_t>::max());
1339 return Some(std::min(max, std::max(*attr, min)));
1342 bool AppWindow::LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight) {
1343 bool gotSize = false;
1345 // if we're the hidden window, don't try to validate our size/position. We're
1346 // special.
1347 if (mIsHiddenWindow) {
1348 return false;
1351 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1352 NS_ENSURE_TRUE(windowElement, false);
1354 // Obtain the sizing information from the <xul:window> element.
1355 aSpecWidth = 100;
1356 aSpecHeight = 100;
1358 if (auto width = ReadSize(*windowElement, nsGkAtoms::width,
1359 nsGkAtoms::minwidth, nsGkAtoms::maxwidth)) {
1360 aSpecWidth = *width;
1361 gotSize = true;
1364 if (auto height = ReadSize(*windowElement, nsGkAtoms::height,
1365 nsGkAtoms::minheight, nsGkAtoms::maxheight)) {
1366 aSpecHeight = *height;
1367 gotSize = true;
1370 return gotSize;
1373 void AppWindow::SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight) {
1374 // constrain to screen size
1375 int32_t screenWidth;
1376 int32_t screenHeight;
1378 if (NS_SUCCEEDED(GetAvailScreenSize(&screenWidth, &screenHeight))) {
1379 if (aSpecWidth > screenWidth) {
1380 aSpecWidth = screenWidth;
1382 if (aSpecHeight > screenHeight) {
1383 aSpecHeight = screenHeight;
1387 NS_ASSERTION(mWindow, "we expected to have a window already");
1389 int32_t currWidth = 0;
1390 int32_t currHeight = 0;
1391 GetSize(&currWidth, &currHeight); // returns device pixels
1393 // convert specified values to device pixels, and resize if needed
1394 double cssToDevPx = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
1395 aSpecWidth = NSToIntRound(aSpecWidth * cssToDevPx);
1396 aSpecHeight = NSToIntRound(aSpecHeight * cssToDevPx);
1397 mIntrinsicallySized = false;
1398 if (aSpecWidth != currWidth || aSpecHeight != currHeight) {
1399 SetSize(aSpecWidth, aSpecHeight, false);
1403 /* Miscellaneous persistent attributes are attributes named in the
1404 |persist| attribute, other than size and position. Those are special
1405 because it's important to load those before one of the misc
1406 attributes (sizemode) and they require extra processing. */
1407 bool AppWindow::UpdateWindowStateFromMiscXULAttributes() {
1408 bool gotState = false;
1410 /* There are no misc attributes of interest to the hidden window.
1411 It's especially important not to try to validate that window's
1412 size or position, because some platforms (Mac OS X) need to
1413 make it visible and offscreen. */
1414 if (mIsHiddenWindow) return false;
1416 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1417 NS_ENSURE_TRUE(windowElement, false);
1419 nsAutoString stateString;
1420 nsSizeMode sizeMode = nsSizeMode_Normal;
1422 // If we are told to ignore the size mode attribute, force
1423 // normal sizemode.
1424 if (mIgnoreXULSizeMode) {
1425 windowElement->SetAttribute(MODE_ATTRIBUTE, SIZEMODE_NORMAL,
1426 IgnoreErrors());
1427 } else {
1428 // Otherwise, read sizemode from DOM and, if the window is resizable,
1429 // set it later.
1430 windowElement->GetAttribute(MODE_ATTRIBUTE, stateString);
1431 if ((stateString.Equals(SIZEMODE_MAXIMIZED) ||
1432 stateString.Equals(SIZEMODE_FULLSCREEN))) {
1433 /* Honor request to maximize only if the window is sizable.
1434 An unsizable, unmaximizable, yet maximized window confuses
1435 Windows OS and is something of a travesty, anyway. */
1436 if (mChromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
1437 mIntrinsicallySized = false;
1439 if (stateString.Equals(SIZEMODE_MAXIMIZED))
1440 sizeMode = nsSizeMode_Maximized;
1441 else
1442 sizeMode = nsSizeMode_Fullscreen;
1447 if (sizeMode == nsSizeMode_Fullscreen) {
1448 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1449 GetWindowDOMWindow(getter_AddRefs(ourWindow));
1450 auto* piWindow = nsPIDOMWindowOuter::From(ourWindow);
1451 piWindow->SetFullScreen(true);
1452 } else {
1453 // For maximized windows, ignore the XUL size and position attributes,
1454 // as setting them would set the window back to normal sizemode.
1455 if (sizeMode == nsSizeMode_Maximized) {
1456 mIgnoreXULSize = true;
1457 mIgnoreXULPosition = true;
1459 mWindow->SetSizeMode(sizeMode);
1461 gotState = true;
1463 // zlevel
1464 windowElement->GetAttribute(ZLEVEL_ATTRIBUTE, stateString);
1465 if (!stateString.IsEmpty()) {
1466 nsresult errorCode;
1467 int32_t zLevel = stateString.ToInteger(&errorCode);
1468 if (NS_SUCCEEDED(errorCode) && zLevel >= lowestZ && zLevel <= highestZ)
1469 SetZLevel(zLevel);
1472 return gotState;
1475 /* Stagger windows of the same type so they don't appear on top of each other.
1476 This code does have a scary double loop -- it'll keep passing through
1477 the entire list of open windows until it finds a non-collision. Doesn't
1478 seem to be a problem, but it deserves watching.
1479 The aRequested{X,Y} parameters here are in desktop pixels;
1480 the aSpec{Width,Height} parameters are CSS pixel dimensions.
1482 void AppWindow::StaggerPosition(int32_t& aRequestedX, int32_t& aRequestedY,
1483 int32_t aSpecWidth, int32_t aSpecHeight) {
1484 // These "constants" will be converted from CSS to desktop pixels
1485 // for the appropriate screen, assuming we find a screen to use...
1486 // hence they're not actually declared const here.
1487 int32_t kOffset = 22;
1488 uint32_t kSlop = 4;
1490 bool keepTrying;
1491 int bouncedX = 0, // bounced off vertical edge of screen
1492 bouncedY = 0; // bounced off horizontal edge
1494 // look for any other windows of this type
1495 nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
1496 if (!wm) return;
1498 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1499 if (!windowElement) return;
1501 nsCOMPtr<nsIAppWindow> ourAppWindow(this);
1503 nsAutoString windowType;
1504 windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, windowType);
1506 int32_t screenTop = 0, // it's pointless to initialize these ...
1507 screenRight = 0, // ... but to prevent oversalubrious and ...
1508 screenBottom = 0, // ... underbright compilers from ...
1509 screenLeft = 0; // ... issuing warnings.
1510 bool gotScreen = false;
1512 { // fetch screen coordinates
1513 nsCOMPtr<nsIScreenManager> screenMgr(
1514 do_GetService("@mozilla.org/gfx/screenmanager;1"));
1515 if (screenMgr) {
1516 nsCOMPtr<nsIScreen> ourScreen;
1517 // the coordinates here are already display pixels
1518 screenMgr->ScreenForRect(aRequestedX, aRequestedY, aSpecWidth,
1519 aSpecHeight, getter_AddRefs(ourScreen));
1520 if (ourScreen) {
1521 int32_t screenWidth, screenHeight;
1522 ourScreen->GetAvailRectDisplayPix(&screenLeft, &screenTop, &screenWidth,
1523 &screenHeight);
1524 screenBottom = screenTop + screenHeight;
1525 screenRight = screenLeft + screenWidth;
1526 // Get the screen's scaling factors and convert staggering constants
1527 // from CSS px to desktop pixel units
1528 double desktopToDeviceScale = 1.0, cssToDeviceScale = 1.0;
1529 ourScreen->GetContentsScaleFactor(&desktopToDeviceScale);
1530 ourScreen->GetDefaultCSSScaleFactor(&cssToDeviceScale);
1531 double cssToDesktopFactor = cssToDeviceScale / desktopToDeviceScale;
1532 kOffset = NSToIntRound(kOffset * cssToDesktopFactor);
1533 kSlop = NSToIntRound(kSlop * cssToDesktopFactor);
1534 // Convert dimensions from CSS to desktop pixels
1535 aSpecWidth = NSToIntRound(aSpecWidth * cssToDesktopFactor);
1536 aSpecHeight = NSToIntRound(aSpecHeight * cssToDesktopFactor);
1537 gotScreen = true;
1542 // One full pass through all windows of this type, repeat until no collisions.
1543 do {
1544 keepTrying = false;
1545 nsCOMPtr<nsISimpleEnumerator> windowList;
1546 wm->GetAppWindowEnumerator(windowType.get(), getter_AddRefs(windowList));
1548 if (!windowList) break;
1550 // One full pass through all windows of this type, offset and stop on
1551 // collision.
1552 do {
1553 bool more;
1554 windowList->HasMoreElements(&more);
1555 if (!more) break;
1557 nsCOMPtr<nsISupports> supportsWindow;
1558 windowList->GetNext(getter_AddRefs(supportsWindow));
1560 nsCOMPtr<nsIAppWindow> listAppWindow(do_QueryInterface(supportsWindow));
1561 if (listAppWindow != ourAppWindow) {
1562 int32_t listX, listY;
1563 nsCOMPtr<nsIBaseWindow> listBaseWindow(
1564 do_QueryInterface(supportsWindow));
1565 listBaseWindow->GetPosition(&listX, &listY);
1566 double scale;
1567 if (NS_SUCCEEDED(
1568 listBaseWindow->GetDevicePixelsPerDesktopPixel(&scale))) {
1569 listX = NSToIntRound(listX / scale);
1570 listY = NSToIntRound(listY / scale);
1573 if (Abs(listX - aRequestedX) <= kSlop &&
1574 Abs(listY - aRequestedY) <= kSlop) {
1575 // collision! offset and start over
1576 if (bouncedX & 0x1)
1577 aRequestedX -= kOffset;
1578 else
1579 aRequestedX += kOffset;
1580 aRequestedY += kOffset;
1582 if (gotScreen) {
1583 // if we're moving to the right and we need to bounce...
1584 if (!(bouncedX & 0x1) &&
1585 ((aRequestedX + aSpecWidth) > screenRight)) {
1586 aRequestedX = screenRight - aSpecWidth;
1587 ++bouncedX;
1590 // if we're moving to the left and we need to bounce...
1591 if ((bouncedX & 0x1) && aRequestedX < screenLeft) {
1592 aRequestedX = screenLeft;
1593 ++bouncedX;
1596 // if we hit the bottom then bounce to the top
1597 if (aRequestedY + aSpecHeight > screenBottom) {
1598 aRequestedY = screenTop;
1599 ++bouncedY;
1603 /* loop around again,
1604 but it's time to give up once we've covered the screen.
1605 there's a potential infinite loop with lots of windows. */
1606 keepTrying = bouncedX < 2 || bouncedY == 0;
1607 break;
1610 } while (true);
1611 } while (keepTrying);
1614 void AppWindow::SyncAttributesToWidget() {
1615 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1616 if (!windowElement) return;
1618 MOZ_DIAGNOSTIC_ASSERT(mWindow, "No widget on SyncAttributesToWidget?");
1620 nsAutoString attr;
1622 // "hidechrome" attribute
1623 if (windowElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidechrome,
1624 nsGkAtoms::_true, eCaseMatters)) {
1625 mWindow->HideWindowChrome(true);
1628 NS_ENSURE_TRUE_VOID(mWindow);
1630 // "chromemargin" attribute
1631 nsIntMargin margins;
1632 windowElement->GetAttribute(u"chromemargin"_ns, attr);
1633 if (nsContentUtils::ParseIntMarginValue(attr, margins)) {
1634 LayoutDeviceIntMargin tmp =
1635 LayoutDeviceIntMargin::FromUnknownMargin(margins);
1636 mWindow->SetNonClientMargins(tmp);
1639 NS_ENSURE_TRUE_VOID(mWindow);
1641 // "windowtype" attribute
1642 windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, attr);
1643 if (!attr.IsEmpty()) {
1644 mWindow->SetWindowClass(attr);
1647 NS_ENSURE_TRUE_VOID(mWindow);
1649 // "icon" attribute
1650 windowElement->GetAttribute(u"icon"_ns, attr);
1651 if (!attr.IsEmpty()) {
1652 mWindow->SetIcon(attr);
1654 NS_ENSURE_TRUE_VOID(mWindow);
1657 // "drawtitle" attribute
1658 windowElement->GetAttribute(u"drawtitle"_ns, attr);
1659 mWindow->SetDrawsTitle(attr.LowerCaseEqualsLiteral("true"));
1661 NS_ENSURE_TRUE_VOID(mWindow);
1663 // "toggletoolbar" attribute
1664 windowElement->GetAttribute(u"toggletoolbar"_ns, attr);
1665 mWindow->SetShowsToolbarButton(attr.LowerCaseEqualsLiteral("true"));
1667 NS_ENSURE_TRUE_VOID(mWindow);
1669 // "macnativefullscreen" attribute
1670 windowElement->GetAttribute(u"macnativefullscreen"_ns, attr);
1671 mWindow->SetSupportsNativeFullscreen(attr.LowerCaseEqualsLiteral("true"));
1673 NS_ENSURE_TRUE_VOID(mWindow);
1675 // "macanimationtype" attribute
1676 windowElement->GetAttribute(u"macanimationtype"_ns, attr);
1677 if (attr.EqualsLiteral("document")) {
1678 mWindow->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation);
1682 enum class ConversionDirection {
1683 InnerToOuter,
1684 OuterToInner,
1687 static void ConvertWindowSize(nsIAppWindow* aWin, const nsAtom* aAttr,
1688 ConversionDirection aDirection,
1689 nsAString& aInOutString) {
1690 MOZ_ASSERT(aWin);
1691 MOZ_ASSERT(aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height);
1693 nsresult rv;
1694 int32_t size = aInOutString.ToInteger(&rv);
1695 if (NS_FAILED(rv)) {
1696 return;
1699 int32_t sizeDiff = aAttr == nsGkAtoms::width
1700 ? aWin->GetOuterToInnerWidthDifferenceInCSSPixels()
1701 : aWin->GetOuterToInnerHeightDifferenceInCSSPixels();
1703 if (!sizeDiff) {
1704 return;
1707 int32_t multiplier = aDirection == ConversionDirection::InnerToOuter ? 1 : -1;
1709 CopyASCIItoUTF16(nsPrintfCString("%d", size + multiplier * sizeDiff),
1710 aInOutString);
1713 nsresult AppWindow::GetPersistentValue(const nsAtom* aAttr, nsAString& aValue) {
1714 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1715 if (!docShellElement) {
1716 return NS_ERROR_FAILURE;
1719 nsAutoString windowElementId;
1720 docShellElement->GetId(windowElementId);
1721 // Elements must have an ID to be persisted.
1722 if (windowElementId.IsEmpty()) {
1723 return NS_OK;
1726 RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
1727 nsIURI* docURI = ownerDoc->GetDocumentURI();
1728 if (!docURI) {
1729 return NS_ERROR_FAILURE;
1731 nsAutoCString utf8uri;
1732 nsresult rv = docURI->GetSpec(utf8uri);
1733 NS_ENSURE_SUCCESS(rv, rv);
1734 NS_ConvertUTF8toUTF16 uri(utf8uri);
1736 #ifdef MOZ_NEW_XULSTORE
1737 nsDependentAtomString attrString(aAttr);
1738 rv = XULStore::GetValue(uri, windowElementId, attrString, aValue);
1739 #else
1740 if (!mLocalStore) {
1741 mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1742 if (NS_WARN_IF(!mLocalStore)) {
1743 return NS_ERROR_NOT_INITIALIZED;
1747 rv = mLocalStore->GetValue(uri, windowElementId, nsDependentAtomString(aAttr),
1748 aValue);
1749 #endif
1750 if (NS_WARN_IF(NS_FAILED(rv))) {
1751 return rv;
1754 if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
1755 // Convert attributes from outer size to inner size for top-level
1756 // windows, see bug 1444525 & co.
1757 ConvertWindowSize(this, aAttr, ConversionDirection::OuterToInner, aValue);
1760 return NS_OK;
1763 nsresult AppWindow::GetDocXulStoreKeys(nsString& aUriSpec,
1764 nsString& aWindowElementId) {
1765 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1766 if (!docShellElement) {
1767 return NS_ERROR_FAILURE;
1770 docShellElement->GetId(aWindowElementId);
1771 // Match the behavior of XULPersist and only persist values if the element
1772 // has an ID.
1773 if (aWindowElementId.IsEmpty()) {
1774 return NS_OK;
1777 RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
1778 nsIURI* docURI = ownerDoc->GetDocumentURI();
1779 if (!docURI) {
1780 return NS_ERROR_FAILURE;
1783 nsAutoCString utf8uri;
1784 nsresult rv = docURI->GetSpec(utf8uri);
1785 if (NS_WARN_IF(NS_FAILED(rv))) {
1786 return rv;
1789 aUriSpec = NS_ConvertUTF8toUTF16(utf8uri);
1791 return NS_OK;
1794 nsresult AppWindow::MaybeSaveEarlyWindowPersistentValues(
1795 const LayoutDeviceIntRect& aRect) {
1796 #ifdef XP_WIN
1797 nsAutoString uri;
1798 nsAutoString windowElementId;
1799 nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
1801 if (NS_WARN_IF(NS_FAILED(rv))) {
1802 return rv;
1805 if (!windowElementId.EqualsLiteral("main-window") ||
1806 !uri.EqualsLiteral("chrome://browser/content/browser.xhtml")) {
1807 return NS_OK;
1810 SkeletonUISettings settings;
1812 settings.screenX = aRect.X();
1813 settings.screenY = aRect.Y();
1814 settings.width = aRect.Width();
1815 settings.height = aRect.Height();
1817 settings.maximized = mWindow->SizeMode() == nsSizeMode_Maximized;
1818 settings.cssToDevPixelScaling = mWindow->GetDefaultScale().scale;
1820 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1821 Document* doc = windowElement->GetComposedDoc();
1822 Element* urlbarEl = doc->GetElementById(u"urlbar"_ns);
1824 nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow();
1825 nsCOMPtr<nsIDOMWindowUtils> utils =
1826 nsGlobalWindowOuter::Cast(window)->WindowUtils();
1827 RefPtr<dom::DOMRect> urlbarRect;
1828 rv = utils->GetBoundsWithoutFlushing(urlbarEl, getter_AddRefs(urlbarRect));
1829 if (NS_WARN_IF(NS_FAILED(rv))) {
1830 return rv;
1833 double urlbarX = urlbarRect->X();
1834 double urlbarWidth = urlbarRect->Width();
1836 // Hard-coding the following values and this behavior in general is rather
1837 // fragile, and can easily get out of sync with the actual front-end values.
1838 // This is not intended as a long-term solution, but only as the relatively
1839 // straightforward implementation of an experimental feature. If we want to
1840 // ship the skeleton UI to all users, we should strongly consider a more
1841 // robust solution than this. The vertical position of the urlbar will be
1842 // fixed.
1843 nsAutoString attributeValue;
1844 urlbarEl->GetAttribute(u"breakout-extend"_ns, attributeValue);
1845 // Scale down the urlbar if it is focused
1846 if (attributeValue.EqualsLiteral("true")) {
1847 // defined in browser.inc.css as 2px
1848 int urlbarBreakoutExtend = 2;
1849 // defined in urlbar-searchbar.inc.css as 5px
1850 int urlbarMarginInline = 5;
1852 // breakout-extend measurements are defined in urlbar-searchbar.inc.css
1853 urlbarX += (double)(urlbarBreakoutExtend + urlbarMarginInline);
1854 urlbarWidth -= (double)(2 * (urlbarBreakoutExtend + urlbarMarginInline));
1856 CSSPixelSpan urlbar;
1857 urlbar.start = urlbarX;
1858 urlbar.end = urlbar.start + urlbarWidth;
1859 settings.urlbarSpan = urlbar;
1861 Element* navbar = doc->GetElementById(u"nav-bar"_ns);
1863 Element* searchbarEl = doc->GetElementById(u"searchbar"_ns);
1864 CSSPixelSpan searchbar;
1865 if (navbar->Contains(searchbarEl)) {
1866 RefPtr<dom::DOMRect> searchbarRect;
1867 rv = utils->GetBoundsWithoutFlushing(searchbarEl,
1868 getter_AddRefs(searchbarRect));
1869 if (NS_WARN_IF(NS_FAILED(rv))) {
1870 return rv;
1872 searchbar.start = searchbarRect->X();
1873 searchbar.end = searchbar.start + searchbarRect->Width();
1874 } else {
1875 // There is no searchbar in the UI
1876 searchbar.start = 0;
1877 searchbar.end = 0;
1879 settings.searchbarSpan = searchbar;
1881 nsAutoString bookmarksVisibility;
1882 Preferences::GetString("browser.toolbars.bookmarks.visibility",
1883 bookmarksVisibility);
1884 settings.bookmarksToolbarShown =
1885 bookmarksVisibility.EqualsLiteral("always") ||
1886 (Preferences::GetBool("browser.toolbars.bookmarks.2h2020", false) &&
1887 bookmarksVisibility.EqualsLiteral("newtab"));
1889 Element* menubar = doc->GetElementById(u"toolbar-menubar"_ns);
1890 menubar->GetAttribute(u"autohide"_ns, attributeValue);
1891 settings.menubarShown = attributeValue.EqualsLiteral("false");
1893 ErrorResult err;
1894 nsCOMPtr<nsIHTMLCollection> toolbarSprings = navbar->GetElementsByTagNameNS(
1895 u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns,
1896 u"toolbarspring"_ns, err);
1897 if (err.Failed()) {
1898 return NS_ERROR_FAILURE;
1900 mozilla::Vector<CSSPixelSpan> springs;
1901 for (int i = 0; i < toolbarSprings->Length(); i++) {
1902 RefPtr<Element> springEl = toolbarSprings->Item(i);
1903 RefPtr<dom::DOMRect> springRect;
1904 rv = utils->GetBoundsWithoutFlushing(springEl, getter_AddRefs(springRect));
1905 if (NS_WARN_IF(NS_FAILED(rv))) {
1906 return rv;
1908 CSSPixelSpan spring;
1909 spring.start = springRect->X();
1910 spring.end = spring.start + springRect->Width();
1911 if (!settings.springs.append(spring)) {
1912 return NS_ERROR_FAILURE;
1916 settings.rtlEnabled = intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1918 bool isInTabletMode = false;
1919 bool autoTouchModePref =
1920 Preferences::GetBool("browser.touchmode.auto", false);
1921 if (autoTouchModePref) {
1922 nsCOMPtr<nsIWindowsUIUtils> uiUtils(
1923 do_GetService("@mozilla.org/windows-ui-utils;1"));
1924 if (!NS_WARN_IF(!uiUtils)) {
1925 uiUtils->GetInTabletMode(&isInTabletMode);
1929 if (isInTabletMode) {
1930 settings.uiDensity = SkeletonUIDensity::Touch;
1931 } else {
1932 int uiDensityPref = Preferences::GetInt("browser.uidensity", 0);
1933 switch (uiDensityPref) {
1934 case 0: {
1935 settings.uiDensity = SkeletonUIDensity::Default;
1936 break;
1938 case 1: {
1939 settings.uiDensity = SkeletonUIDensity::Compact;
1940 break;
1942 case 2: {
1943 settings.uiDensity = SkeletonUIDensity::Touch;
1944 break;
1949 Unused << PersistPreXULSkeletonUIValues(settings);
1950 #endif
1952 return NS_OK;
1955 nsresult AppWindow::SetPersistentValue(const nsAtom* aAttr,
1956 const nsAString& aValue) {
1957 nsAutoString uri;
1958 nsAutoString windowElementId;
1959 nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
1961 if (NS_FAILED(rv) || windowElementId.IsEmpty()) {
1962 return rv;
1965 nsAutoString maybeConvertedValue(aValue);
1966 if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
1967 // Make sure we store the <window> attributes as outer window size, see
1968 // bug 1444525 & co.
1969 ConvertWindowSize(this, aAttr, ConversionDirection::InnerToOuter,
1970 maybeConvertedValue);
1973 #ifdef MOZ_NEW_XULSTORE
1974 nsDependentAtomString attrString(aAttr);
1975 return XULStore::SetValue(uri, windowElementId, attrString,
1976 maybeConvertedValue);
1977 #else
1978 if (!mLocalStore) {
1979 mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1980 if (NS_WARN_IF(!mLocalStore)) {
1981 return NS_ERROR_NOT_INITIALIZED;
1985 return mLocalStore->SetValue(
1986 uri, windowElementId, nsDependentAtomString(aAttr), maybeConvertedValue);
1987 #endif
1990 NS_IMETHODIMP AppWindow::SavePersistentAttributes() {
1991 // can happen when the persistence timer fires at an inopportune time
1992 // during window shutdown
1993 if (!mDocShell) return NS_ERROR_FAILURE;
1995 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1996 if (!docShellElement) return NS_ERROR_FAILURE;
1998 nsAutoString persistString;
1999 docShellElement->GetAttribute(PERSIST_ATTRIBUTE, persistString);
2000 if (persistString.IsEmpty()) { // quick check which sometimes helps
2001 mPersistentAttributesDirty = 0;
2002 return NS_OK;
2005 bool isFullscreen = false;
2006 if (nsPIDOMWindowOuter* domWindow = mDocShell->GetWindow()) {
2007 isFullscreen = domWindow->GetFullScreen();
2010 // get our size, position and mode to persist
2011 LayoutDeviceIntRect rect;
2012 bool gotRestoredBounds = NS_SUCCEEDED(mWindow->GetRestoredBounds(rect));
2014 // we use CSS pixels for size, but desktop pixels for position
2015 CSSToLayoutDeviceScale sizeScale = mWindow->GetDefaultScale();
2016 DesktopToLayoutDeviceScale posScale = mWindow->GetDesktopToDeviceScale();
2018 // make our position relative to our parent, if any
2019 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
2020 if (parent && gotRestoredBounds) {
2021 int32_t parentX, parentY;
2022 if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
2023 rect.MoveBy(-parentX, -parentY);
2027 nsAutoString sizeString;
2028 bool shouldPersist = !isFullscreen;
2029 ErrorResult rv;
2030 // (only for size elements which are persisted)
2031 if ((mPersistentAttributesDirty & PAD_POSITION) && gotRestoredBounds) {
2032 if (persistString.Find("screenX") >= 0) {
2033 sizeString.Truncate();
2034 sizeString.AppendInt(NSToIntRound(rect.X() / posScale.scale));
2035 docShellElement->SetAttribute(SCREENX_ATTRIBUTE, sizeString, rv);
2036 if (shouldPersist) {
2037 Unused << SetPersistentValue(nsGkAtoms::screenX, sizeString);
2040 if (persistString.Find("screenY") >= 0) {
2041 sizeString.Truncate();
2042 sizeString.AppendInt(NSToIntRound(rect.Y() / posScale.scale));
2043 docShellElement->SetAttribute(SCREENY_ATTRIBUTE, sizeString, rv);
2044 if (shouldPersist) {
2045 Unused << SetPersistentValue(nsGkAtoms::screenY, sizeString);
2050 if ((mPersistentAttributesDirty & PAD_SIZE) && gotRestoredBounds) {
2051 LayoutDeviceIntRect innerRect =
2052 rect - GetOuterToInnerSizeDifference(mWindow);
2053 if (persistString.Find("width") >= 0) {
2054 sizeString.Truncate();
2055 sizeString.AppendInt(NSToIntRound(innerRect.Width() / sizeScale.scale));
2056 docShellElement->SetAttribute(WIDTH_ATTRIBUTE, sizeString, rv);
2057 if (shouldPersist) {
2058 Unused << SetPersistentValue(nsGkAtoms::width, sizeString);
2061 if (persistString.Find("height") >= 0) {
2062 sizeString.Truncate();
2063 sizeString.AppendInt(NSToIntRound(innerRect.Height() / sizeScale.scale));
2064 docShellElement->SetAttribute(HEIGHT_ATTRIBUTE, sizeString, rv);
2065 if (shouldPersist) {
2066 Unused << SetPersistentValue(nsGkAtoms::height, sizeString);
2071 Unused << MaybeSaveEarlyWindowPersistentValues(rect);
2073 if (mPersistentAttributesDirty & PAD_MISC) {
2074 nsSizeMode sizeMode = mWindow->SizeMode();
2076 if (sizeMode != nsSizeMode_Minimized) {
2077 if (sizeMode == nsSizeMode_Maximized)
2078 sizeString.Assign(SIZEMODE_MAXIMIZED);
2079 else if (sizeMode == nsSizeMode_Fullscreen)
2080 sizeString.Assign(SIZEMODE_FULLSCREEN);
2081 else
2082 sizeString.Assign(SIZEMODE_NORMAL);
2083 docShellElement->SetAttribute(MODE_ATTRIBUTE, sizeString, rv);
2084 if (shouldPersist && persistString.Find("sizemode") >= 0) {
2085 Unused << SetPersistentValue(nsGkAtoms::sizemode, sizeString);
2088 bool tiled = mWindow->IsTiled();
2089 if (tiled) {
2090 sizeString.Assign(u"true"_ns);
2091 } else {
2092 sizeString.Assign(u"false"_ns);
2094 docShellElement->SetAttribute(TILED_ATTRIBUTE, sizeString, rv);
2095 if (persistString.Find("zlevel") >= 0) {
2096 uint32_t zLevel;
2097 nsCOMPtr<nsIWindowMediator> mediator(
2098 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
2099 if (mediator) {
2100 mediator->GetZLevel(this, &zLevel);
2101 sizeString.Truncate();
2102 sizeString.AppendInt(zLevel);
2103 docShellElement->SetAttribute(ZLEVEL_ATTRIBUTE, sizeString, rv);
2104 if (shouldPersist) {
2105 Unused << SetPersistentValue(nsGkAtoms::zlevel, sizeString);
2111 mPersistentAttributesDirty = 0;
2112 return NS_OK;
2115 NS_IMETHODIMP AppWindow::GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow) {
2116 NS_ENSURE_STATE(mDocShell);
2118 if (!mDOMWindow) mDOMWindow = mDocShell->GetWindow();
2119 NS_ENSURE_TRUE(mDOMWindow, NS_ERROR_FAILURE);
2121 *aDOMWindow = mDOMWindow;
2122 NS_ADDREF(*aDOMWindow);
2123 return NS_OK;
2126 dom::Element* AppWindow::GetWindowDOMElement() const {
2127 NS_ENSURE_TRUE(mDocShell, nullptr);
2129 nsCOMPtr<nsIContentViewer> cv;
2130 mDocShell->GetContentViewer(getter_AddRefs(cv));
2131 NS_ENSURE_TRUE(cv, nullptr);
2133 const dom::Document* document = cv->GetDocument();
2134 NS_ENSURE_TRUE(document, nullptr);
2136 return document->GetRootElement();
2139 nsresult AppWindow::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
2140 bool aPrimary) {
2141 // Set the default content tree owner
2142 if (aPrimary) {
2143 NS_ENSURE_SUCCESS(EnsurePrimaryContentTreeOwner(), NS_ERROR_FAILURE);
2144 aContentShell->SetTreeOwner(mPrimaryContentTreeOwner);
2145 mPrimaryContentShell = aContentShell;
2146 mPrimaryBrowserParent = nullptr;
2147 } else {
2148 NS_ENSURE_SUCCESS(EnsureContentTreeOwner(), NS_ERROR_FAILURE);
2149 aContentShell->SetTreeOwner(mContentTreeOwner);
2150 if (mPrimaryContentShell == aContentShell) mPrimaryContentShell = nullptr;
2153 return NS_OK;
2156 nsresult AppWindow::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
2157 if (mPrimaryContentShell == aContentShell) {
2158 mPrimaryContentShell = nullptr;
2160 return NS_OK;
2163 NS_IMETHODIMP
2164 AppWindow::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
2165 if (mPrimaryBrowserParent) {
2166 return GetPrimaryRemoteTabSize(aWidth, aHeight);
2167 } else if (mPrimaryContentShell) {
2168 return GetPrimaryContentShellSize(aWidth, aHeight);
2170 return NS_ERROR_UNEXPECTED;
2173 nsresult AppWindow::GetPrimaryRemoteTabSize(int32_t* aWidth, int32_t* aHeight) {
2174 BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
2175 // Need strong ref, since Client* can run script.
2176 RefPtr<dom::Element> element = host->GetOwnerElement();
2177 NS_ENSURE_STATE(element);
2179 *aWidth = element->ClientWidth();
2180 *aHeight = element->ClientHeight();
2181 return NS_OK;
2184 nsresult AppWindow::GetPrimaryContentShellSize(int32_t* aWidth,
2185 int32_t* aHeight) {
2186 NS_ENSURE_STATE(mPrimaryContentShell);
2188 nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(mPrimaryContentShell));
2189 NS_ENSURE_STATE(shellWindow);
2191 int32_t devicePixelWidth, devicePixelHeight;
2192 double shellScale = 1.0;
2193 // We want to return CSS pixels. First, we get device pixels
2194 // from the content area...
2195 shellWindow->GetSize(&devicePixelWidth, &devicePixelHeight);
2196 // And then get the device pixel scaling factor. Dividing device
2197 // pixels by this scaling factor gives us CSS pixels.
2198 shellWindow->GetUnscaledDevicePixelsPerCSSPixel(&shellScale);
2199 *aWidth = NSToIntRound(devicePixelWidth / shellScale);
2200 *aHeight = NSToIntRound(devicePixelHeight / shellScale);
2201 return NS_OK;
2204 NS_IMETHODIMP
2205 AppWindow::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
2206 if (mPrimaryBrowserParent) {
2207 return SetPrimaryRemoteTabSize(aWidth, aHeight);
2208 } else if (mPrimaryContentShell) {
2209 return SizeShellTo(mPrimaryContentShell, aWidth, aHeight);
2211 return NS_ERROR_UNEXPECTED;
2214 nsresult AppWindow::SetPrimaryRemoteTabSize(int32_t aWidth, int32_t aHeight) {
2215 int32_t shellWidth, shellHeight;
2216 GetPrimaryRemoteTabSize(&shellWidth, &shellHeight);
2218 double scale = 1.0;
2219 GetUnscaledDevicePixelsPerCSSPixel(&scale);
2221 SizeShellToWithLimit(aWidth, aHeight, shellWidth * scale,
2222 shellHeight * scale);
2223 return NS_OK;
2226 nsresult AppWindow::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
2227 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
2228 return mDocShell->GetSize(aWidth, aHeight);
2231 nsresult AppWindow::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
2232 return SizeShellTo(mDocShell, aWidth, aHeight);
2235 NS_IMETHODIMP AppWindow::SizeShellTo(nsIDocShellTreeItem* aShellItem,
2236 int32_t aCX, int32_t aCY) {
2237 // XXXTAB This is wrong, we should actually reflow based on the passed in
2238 // shell. For now we are hacking and doing delta sizing. This is bad
2239 // because it assumes all size we add will go to the shell which probably
2240 // won't happen.
2242 nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
2243 NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
2245 int32_t width = 0;
2246 int32_t height = 0;
2247 shellAsWin->GetSize(&width, &height);
2249 SizeShellToWithLimit(aCX, aCY, width, height);
2251 return NS_OK;
2254 NS_IMETHODIMP AppWindow::ExitModalLoop(nsresult aStatus) {
2255 if (mContinueModalLoop) EnableParent(true);
2256 mContinueModalLoop = false;
2257 mModalStatus = aStatus;
2258 return NS_OK;
2261 // top-level function to create a new window
2262 NS_IMETHODIMP AppWindow::CreateNewWindow(int32_t aChromeFlags,
2263 nsIOpenWindowInfo* aOpenWindowInfo,
2264 nsIAppWindow** _retval) {
2265 NS_ENSURE_ARG_POINTER(_retval);
2267 if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME) {
2268 MOZ_RELEASE_ASSERT(
2269 !aOpenWindowInfo,
2270 "Unexpected nsOpenWindowInfo when creating a new chrome window");
2271 return CreateNewChromeWindow(aChromeFlags, _retval);
2274 return CreateNewContentWindow(aChromeFlags, aOpenWindowInfo, _retval);
2277 NS_IMETHODIMP AppWindow::CreateNewChromeWindow(int32_t aChromeFlags,
2278 nsIAppWindow** _retval) {
2279 nsCOMPtr<nsIAppShellService> appShell(
2280 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
2281 NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
2283 // Just do a normal create of a window and return.
2284 nsCOMPtr<nsIAppWindow> newWindow;
2285 appShell->CreateTopLevelWindow(
2286 this, nullptr, aChromeFlags, nsIAppShellService::SIZE_TO_CONTENT,
2287 nsIAppShellService::SIZE_TO_CONTENT, getter_AddRefs(newWindow));
2289 NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
2291 newWindow.forget(_retval);
2293 return NS_OK;
2296 NS_IMETHODIMP AppWindow::CreateNewContentWindow(
2297 int32_t aChromeFlags, nsIOpenWindowInfo* aOpenWindowInfo,
2298 nsIAppWindow** _retval) {
2299 nsCOMPtr<nsIAppShellService> appShell(
2300 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
2301 NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
2303 // We need to create a new top level window and then enter a nested
2304 // loop. Eventually the new window will be told that it has loaded,
2305 // at which time we know it is safe to spin out of the nested loop
2306 // and allow the opening code to proceed.
2308 nsCOMPtr<nsIURI> uri;
2309 nsAutoCString urlStr;
2310 urlStr.AssignLiteral(BROWSER_CHROME_URL_QUOTED);
2312 nsCOMPtr<nsIIOService> service(do_GetService(NS_IOSERVICE_CONTRACTID));
2313 if (service) {
2314 service->NewURI(urlStr, nullptr, nullptr, getter_AddRefs(uri));
2316 NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
2318 // We need to create a chrome window to contain the content window we're about
2319 // to pass back. The subject principal needs to be system while we're creating
2320 // it to make things work right, so force a system caller. See bug 799348
2321 // comment 13 for a description of what happens when we don't.
2322 nsCOMPtr<nsIAppWindow> newWindow;
2324 AutoNoJSAPI nojsapi;
2325 appShell->CreateTopLevelWindow(this, uri, aChromeFlags, 615, 480,
2326 getter_AddRefs(newWindow));
2327 NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
2330 AppWindow* appWin =
2331 static_cast<AppWindow*>(static_cast<nsIAppWindow*>(newWindow));
2333 // Specify which flags should be used by browser.xhtml to create the initial
2334 // content browser window.
2335 appWin->mInitialOpenWindowInfo = aOpenWindowInfo;
2337 // Specify that we want the window to remain locked until the chrome has
2338 // loaded.
2339 appWin->LockUntilChromeLoad();
2342 AutoNoJSAPI nojsapi;
2343 SpinEventLoopUntil([&]() { return !appWin->IsLocked(); });
2346 NS_ENSURE_STATE(appWin->mPrimaryContentShell ||
2347 appWin->mPrimaryBrowserParent);
2348 MOZ_ASSERT_IF(appWin->mPrimaryContentShell,
2349 !aOpenWindowInfo->GetNextRemoteBrowser());
2351 newWindow.forget(_retval);
2353 return NS_OK;
2356 NS_IMETHODIMP AppWindow::GetHasPrimaryContent(bool* aResult) {
2357 *aResult = mPrimaryBrowserParent || mPrimaryContentShell;
2358 return NS_OK;
2361 void AppWindow::EnableParent(bool aEnable) {
2362 nsCOMPtr<nsIBaseWindow> parentWindow;
2363 nsCOMPtr<nsIWidget> parentWidget;
2365 parentWindow = do_QueryReferent(mParentWindow);
2366 if (parentWindow) parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
2367 if (parentWidget) parentWidget->Enable(aEnable);
2370 // Constrain the window to its proper z-level
2371 bool AppWindow::ConstrainToZLevel(bool aImmediate, nsWindowZ* aPlacement,
2372 nsIWidget* aReqBelow,
2373 nsIWidget** aActualBelow) {
2374 #if 0
2375 /* Do we have a parent window? This means our z-order is already constrained,
2376 since we're a dependent window. Our window list isn't hierarchical,
2377 so we can't properly calculate placement for such a window.
2378 Should we just abort? */
2379 nsCOMPtr<nsIBaseWindow> parentWindow = do_QueryReferent(mParentWindow);
2380 if (parentWindow)
2381 return false;
2382 #endif
2384 nsCOMPtr<nsIWindowMediator> mediator(
2385 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
2386 if (!mediator) return false;
2388 bool altered;
2389 uint32_t position, newPosition, zLevel;
2390 nsIAppWindow* us = this;
2392 altered = false;
2393 mediator->GetZLevel(this, &zLevel);
2395 // translate from WidgetGUIEvent to nsIWindowMediator constants
2396 position = nsIWindowMediator::zLevelTop;
2397 if (*aPlacement == nsWindowZBottom || zLevel == nsIAppWindow::lowestZ)
2398 position = nsIWindowMediator::zLevelBottom;
2399 else if (*aPlacement == nsWindowZRelative)
2400 position = nsIWindowMediator::zLevelBelow;
2402 if (NS_SUCCEEDED(mediator->CalculateZPosition(
2403 us, position, aReqBelow, &newPosition, aActualBelow, &altered))) {
2404 /* If we were asked to move to the top but constrained to remain
2405 below one of our other windows, first move all windows in that
2406 window's layer and above to the top. This allows the user to
2407 click a window which can't be topmost and still bring mozilla
2408 to the foreground. */
2409 if (altered &&
2410 (position == nsIWindowMediator::zLevelTop ||
2411 (position == nsIWindowMediator::zLevelBelow && aReqBelow == 0)))
2412 PlaceWindowLayersBehind(zLevel + 1, nsIAppWindow::highestZ, 0);
2414 if (*aPlacement != nsWindowZBottom &&
2415 position == nsIWindowMediator::zLevelBottom)
2416 altered = true;
2417 if (altered || aImmediate) {
2418 if (newPosition == nsIWindowMediator::zLevelTop)
2419 *aPlacement = nsWindowZTop;
2420 else if (newPosition == nsIWindowMediator::zLevelBottom)
2421 *aPlacement = nsWindowZBottom;
2422 else
2423 *aPlacement = nsWindowZRelative;
2425 if (aImmediate) {
2426 nsCOMPtr<nsIBaseWindow> ourBase = do_QueryObject(this);
2427 if (ourBase) {
2428 nsCOMPtr<nsIWidget> ourWidget;
2429 ourBase->GetMainWidget(getter_AddRefs(ourWidget));
2430 ourWidget->PlaceBehind(*aPlacement == nsWindowZBottom
2431 ? eZPlacementBottom
2432 : eZPlacementBelow,
2433 *aActualBelow, false);
2438 /* CalculateZPosition can tell us to be below nothing, because it tries
2439 not to change something it doesn't recognize. A request to verify
2440 being below an unrecognized window, then, is treated as a request
2441 to come to the top (below null) */
2442 nsCOMPtr<nsIAppWindow> windowAbove;
2443 if (newPosition == nsIWindowMediator::zLevelBelow && *aActualBelow) {
2444 windowAbove = (*aActualBelow)->GetWidgetListener()->GetAppWindow();
2447 mediator->SetZPosition(us, newPosition, windowAbove);
2450 return altered;
2453 /* Re-z-position all windows in the layers from aLowLevel to aHighLevel,
2454 inclusive, to be behind aBehind. aBehind of null means on top.
2455 Note this method actually does nothing to our relative window positions.
2456 (And therefore there's no need to inform WindowMediator we're moving
2457 things, because we aren't.) This method is useful for, say, moving
2458 a range of layers of our own windows relative to windows belonging to
2459 external applications.
2461 void AppWindow::PlaceWindowLayersBehind(uint32_t aLowLevel, uint32_t aHighLevel,
2462 nsIAppWindow* aBehind) {
2463 // step through windows in z-order from top to bottommost window
2465 nsCOMPtr<nsIWindowMediator> mediator(
2466 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
2467 if (!mediator) return;
2469 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
2470 mediator->GetZOrderAppWindowEnumerator(0, true,
2471 getter_AddRefs(windowEnumerator));
2472 if (!windowEnumerator) return;
2474 // each window will be moved behind previousHighWidget, itself
2475 // a moving target. initialize it.
2476 nsCOMPtr<nsIWidget> previousHighWidget;
2477 if (aBehind) {
2478 nsCOMPtr<nsIBaseWindow> highBase(do_QueryInterface(aBehind));
2479 if (highBase) highBase->GetMainWidget(getter_AddRefs(previousHighWidget));
2482 // get next lower window
2483 bool more;
2484 while (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)) && more) {
2485 uint32_t nextZ; // z-level of nextWindow
2486 nsCOMPtr<nsISupports> nextWindow;
2487 windowEnumerator->GetNext(getter_AddRefs(nextWindow));
2488 nsCOMPtr<nsIAppWindow> nextAppWindow(do_QueryInterface(nextWindow));
2489 nextAppWindow->GetZLevel(&nextZ);
2490 if (nextZ < aLowLevel)
2491 break; // we've processed all windows through aLowLevel
2493 // move it just below its next higher window
2494 nsCOMPtr<nsIBaseWindow> nextBase(do_QueryInterface(nextAppWindow));
2495 if (nextBase) {
2496 nsCOMPtr<nsIWidget> nextWidget;
2497 nextBase->GetMainWidget(getter_AddRefs(nextWidget));
2498 if (nextZ <= aHighLevel)
2499 nextWidget->PlaceBehind(eZPlacementBelow, previousHighWidget, false);
2500 previousHighWidget = nextWidget;
2505 void AppWindow::SetContentScrollbarVisibility(bool aVisible) {
2506 nsCOMPtr<nsPIDOMWindowOuter> contentWin(
2507 do_GetInterface(mPrimaryContentShell));
2508 if (!contentWin) {
2509 return;
2512 nsContentUtils::SetScrollbarsVisibility(contentWin->GetDocShell(), aVisible);
2515 // during spinup, attributes that haven't been loaded yet can't be dirty
2516 void AppWindow::PersistentAttributesDirty(uint32_t aDirtyFlags) {
2517 mPersistentAttributesDirty |= aDirtyFlags & mPersistentAttributesMask;
2520 void AppWindow::ApplyChromeFlags() {
2521 nsCOMPtr<dom::Element> window = GetWindowDOMElement();
2522 if (!window) {
2523 return;
2526 if (mChromeLoaded) {
2527 // The two calls in this block don't need to happen early because they
2528 // don't cause a global restyle on the document. Not only that, but the
2529 // scrollbar stuff needs a content area to toggle the scrollbars on anyway.
2530 // So just don't do these until mChromeLoaded is true.
2532 // Scrollbars have their own special treatment.
2533 SetContentScrollbarVisibility(mChromeFlags &
2534 nsIWebBrowserChrome::CHROME_SCROLLBARS);
2537 /* the other flags are handled together. we have style rules
2538 in navigator.css that trigger visibility based on
2539 the 'chromehidden' attribute of the <window> tag. */
2540 nsAutoString newvalue;
2542 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_MENUBAR))
2543 newvalue.AppendLiteral("menubar ");
2545 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_TOOLBAR))
2546 newvalue.AppendLiteral("toolbar ");
2548 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_LOCATIONBAR))
2549 newvalue.AppendLiteral("location ");
2551 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR))
2552 newvalue.AppendLiteral("directories ");
2554 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_STATUSBAR))
2555 newvalue.AppendLiteral("status ");
2557 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_EXTRA))
2558 newvalue.AppendLiteral("extrachrome ");
2560 // Note that if we're not actually changing the value this will be a no-op,
2561 // so no need to compare to the old value.
2562 IgnoredErrorResult rv;
2563 window->SetAttribute(u"chromehidden"_ns, newvalue, rv);
2566 NS_IMETHODIMP
2567 AppWindow::BeforeStartLayout() {
2568 ApplyChromeFlags();
2569 LoadPersistentWindowState();
2570 SyncAttributesToWidget();
2571 if (mWindow) {
2572 SizeShell();
2574 return NS_OK;
2577 NS_IMETHODIMP
2578 AppWindow::LockAspectRatio(bool aShouldLock) {
2579 mWindow->LockAspectRatio(aShouldLock);
2580 return NS_OK;
2583 void AppWindow::LoadPersistentWindowState() {
2584 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
2585 if (!docShellElement) {
2586 return;
2589 // Check if the window wants to persist anything.
2590 nsAutoString persist;
2591 docShellElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
2592 if (persist.IsEmpty()) {
2593 return;
2596 auto loadValue = [&](const nsAtom* aAttr) {
2597 nsDependentAtomString attrString(aAttr);
2598 if (persist.Find(attrString) >= 0) {
2599 nsAutoString value;
2600 nsresult rv = GetPersistentValue(aAttr, value);
2601 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get persistent state.");
2602 if (NS_SUCCEEDED(rv) && !value.IsEmpty()) {
2603 IgnoredErrorResult err;
2604 docShellElement->SetAttribute(attrString, value, err);
2609 loadValue(nsGkAtoms::screenX);
2610 loadValue(nsGkAtoms::screenY);
2611 loadValue(nsGkAtoms::width);
2612 loadValue(nsGkAtoms::height);
2613 loadValue(nsGkAtoms::sizemode);
2616 void AppWindow::SizeShell() {
2617 AutoRestore<bool> sizingShellFromXUL(mSizingShellFromXUL);
2618 mSizingShellFromXUL = true;
2620 int32_t specWidth = -1, specHeight = -1;
2621 bool gotSize = false;
2623 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
2624 nsAutoString windowType;
2625 if (windowElement) {
2626 windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, windowType);
2629 CSSIntSize windowDiff = GetOuterToInnerSizeDifferenceInCSSPixels(mWindow);
2631 // If we're using fingerprint resistance, we're going to resize the window
2632 // once we have primary content.
2633 if (nsContentUtils::ShouldResistFingerprinting() &&
2634 windowType.EqualsLiteral("navigator:browser")) {
2635 // Once we've got primary content, force dimensions.
2636 if (mPrimaryContentShell || mPrimaryBrowserParent) {
2637 ForceRoundedDimensions();
2639 // Always avoid setting size/sizemode on this window.
2640 mIgnoreXULSize = true;
2641 mIgnoreXULSizeMode = true;
2642 } else if (!mIgnoreXULSize) {
2643 gotSize = LoadSizeFromXUL(specWidth, specHeight);
2644 specWidth += windowDiff.width;
2645 specHeight += windowDiff.height;
2648 bool positionSet = !mIgnoreXULPosition;
2649 nsCOMPtr<nsIAppWindow> parentWindow(do_QueryReferent(mParentWindow));
2650 #if defined(XP_UNIX) && !defined(XP_MACOSX)
2651 // don't override WM placement on unix for independent, top-level windows
2652 // (however, we think the benefits of intelligent dependent window placement
2653 // trump that override.)
2654 if (!parentWindow) positionSet = false;
2655 #endif
2656 if (positionSet) {
2657 // We have to do this before sizing the window, because sizing depends
2658 // on the resolution of the screen we're on. But positioning needs to
2659 // know the size so that it can constrain to screen bounds.... as an
2660 // initial guess here, we'll use the specified size (if any).
2661 positionSet = LoadPositionFromXUL(specWidth, specHeight);
2664 if (gotSize) {
2665 SetSpecifiedSize(specWidth, specHeight);
2668 if (mIntrinsicallySized) {
2669 // (if LoadSizeFromXUL set the size, mIntrinsicallySized will be false)
2670 nsCOMPtr<nsIContentViewer> cv;
2671 mDocShell->GetContentViewer(getter_AddRefs(cv));
2672 if (cv) {
2673 RefPtr<nsDocShell> docShell = mDocShell;
2674 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
2675 docShell->GetTreeOwner(getter_AddRefs(treeOwner));
2676 if (treeOwner) {
2677 // GetContentSize can fail, so initialise |width| and |height| to be
2678 // on the safe side.
2679 int32_t width = 0, height = 0;
2680 if (NS_SUCCEEDED(cv->GetContentSize(&width, &height))) {
2681 treeOwner->SizeShellTo(docShell, width, height);
2682 // Update specified size for the final LoadPositionFromXUL call.
2683 specWidth = width + windowDiff.width;
2684 specHeight = height + windowDiff.height;
2690 // Now that we have set the window's final size, we can re-do its
2691 // positioning so that it is properly constrained to the screen.
2692 if (positionSet) {
2693 LoadPositionFromXUL(specWidth, specHeight);
2696 UpdateWindowStateFromMiscXULAttributes();
2698 if (mChromeLoaded && mCenterAfterLoad && !positionSet &&
2699 mWindow->SizeMode() == nsSizeMode_Normal) {
2700 Center(parentWindow, parentWindow ? false : true, false);
2704 NS_IMETHODIMP AppWindow::GetXULBrowserWindow(
2705 nsIXULBrowserWindow** aXULBrowserWindow) {
2706 NS_IF_ADDREF(*aXULBrowserWindow = mXULBrowserWindow);
2707 return NS_OK;
2710 NS_IMETHODIMP AppWindow::SetXULBrowserWindow(
2711 nsIXULBrowserWindow* aXULBrowserWindow) {
2712 mXULBrowserWindow = aXULBrowserWindow;
2713 return NS_OK;
2716 void AppWindow::SizeShellToWithLimit(int32_t aDesiredWidth,
2717 int32_t aDesiredHeight,
2718 int32_t shellItemWidth,
2719 int32_t shellItemHeight) {
2720 int32_t widthDelta = aDesiredWidth - shellItemWidth;
2721 int32_t heightDelta = aDesiredHeight - shellItemHeight;
2723 if (widthDelta || heightDelta) {
2724 int32_t winWidth = 0;
2725 int32_t winHeight = 0;
2727 GetSize(&winWidth, &winHeight);
2728 // There's no point in trying to make the window smaller than the
2729 // desired content area size --- that's not likely to work. This whole
2730 // function assumes that the outer docshell is adding some constant
2731 // "border" chrome to the content area.
2732 winWidth = std::max(winWidth + widthDelta, aDesiredWidth);
2733 winHeight = std::max(winHeight + heightDelta, aDesiredHeight);
2734 SetSize(winWidth, winHeight, true);
2738 nsresult AppWindow::GetTabCount(uint32_t* aResult) {
2739 if (mXULBrowserWindow) {
2740 return mXULBrowserWindow->GetTabCount(aResult);
2743 *aResult = 0;
2744 return NS_OK;
2747 nsresult AppWindow::GetInitialOpenWindowInfo(
2748 nsIOpenWindowInfo** aOpenWindowInfo) {
2749 NS_ENSURE_ARG_POINTER(aOpenWindowInfo);
2750 *aOpenWindowInfo = do_AddRef(mInitialOpenWindowInfo).take();
2751 return NS_OK;
2754 PresShell* AppWindow::GetPresShell() {
2755 if (!mDocShell) {
2756 return nullptr;
2758 return mDocShell->GetPresShell();
2761 bool AppWindow::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) {
2762 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
2763 if (pm) {
2764 nsCOMPtr<nsPIDOMWindowOuter> window =
2765 mDocShell ? mDocShell->GetWindow() : nullptr;
2766 pm->AdjustPopupsOnWindowChange(window);
2769 // Notify all tabs that the widget moved.
2770 if (mDocShell && mDocShell->GetWindow()) {
2771 nsCOMPtr<EventTarget> eventTarget =
2772 mDocShell->GetWindow()->GetTopWindowRoot();
2773 nsContentUtils::DispatchChromeEvent(
2774 mDocShell->GetDocument(), eventTarget, u"MozUpdateWindowPos"_ns,
2775 CanBubble::eNo, Cancelable::eNo, nullptr);
2778 // Persist position, but not immediately, in case this OS is firing
2779 // repeated move events as the user drags the window
2780 SetPersistenceTimer(PAD_POSITION);
2781 return false;
2784 bool AppWindow::WindowResized(nsIWidget* aWidget, int32_t aWidth,
2785 int32_t aHeight) {
2786 if (mDocShell) {
2787 mDocShell->SetPositionAndSize(0, 0, aWidth, aHeight, 0);
2789 // Persist size, but not immediately, in case this OS is firing
2790 // repeated size events as the user drags the sizing handle
2791 if (!IsLocked()) SetPersistenceTimer(PAD_POSITION | PAD_SIZE | PAD_MISC);
2792 // Check if we need to continue a fullscreen change.
2793 switch (mFullscreenChangeState) {
2794 case FullscreenChangeState::WillChange:
2795 mFullscreenChangeState = FullscreenChangeState::WidgetResized;
2796 break;
2797 case FullscreenChangeState::WidgetEnteredFullscreen:
2798 FinishFullscreenChange(true);
2799 break;
2800 case FullscreenChangeState::WidgetExitedFullscreen:
2801 FinishFullscreenChange(false);
2802 break;
2803 case FullscreenChangeState::WidgetResized:
2804 case FullscreenChangeState::NotChanging:
2805 break;
2807 return true;
2810 bool AppWindow::RequestWindowClose(nsIWidget* aWidget) {
2811 // Maintain a reference to this as it is about to get destroyed.
2812 nsCOMPtr<nsIAppWindow> appWindow(this);
2814 nsCOMPtr<nsPIDOMWindowOuter> window(mDocShell ? mDocShell->GetWindow()
2815 : nullptr);
2816 nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window);
2818 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
2819 if (!presShell) {
2820 mozilla::DebugOnly<bool> dying;
2821 MOZ_ASSERT(NS_SUCCEEDED(mDocShell->IsBeingDestroyed(&dying)) && dying,
2822 "No presShell, but window is not being destroyed");
2823 } else if (eventTarget) {
2824 RefPtr<nsPresContext> presContext = presShell->GetPresContext();
2826 nsEventStatus status = nsEventStatus_eIgnore;
2827 WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
2828 if (NS_SUCCEEDED(EventDispatcher::Dispatch(eventTarget, presContext, &event,
2829 nullptr, &status)) &&
2830 status == nsEventStatus_eConsumeNoDefault)
2831 return false;
2834 Destroy();
2835 return false;
2838 void AppWindow::SizeModeChanged(nsSizeMode sizeMode) {
2839 // An alwaysRaised (or higher) window will hide any newly opened normal
2840 // browser windows, so here we just drop a raised window to the normal
2841 // zlevel if it's maximized. We make no provision for automatically
2842 // re-raising it when restored.
2843 if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) {
2844 uint32_t zLevel;
2845 GetZLevel(&zLevel);
2846 if (zLevel > nsIAppWindow::normalZ) SetZLevel(nsIAppWindow::normalZ);
2848 mWindow->SetSizeMode(sizeMode);
2850 // Persist mode, but not immediately, because in many (all?)
2851 // cases this will merge with the similar call in NS_SIZE and
2852 // write the attribute values only once.
2853 SetPersistenceTimer(PAD_MISC);
2854 nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
2855 mDocShell ? mDocShell->GetWindow() : nullptr;
2856 if (ourWindow) {
2857 // Ensure that the fullscreen state is synchronized between
2858 // the widget and the outer window object.
2859 if (sizeMode == nsSizeMode_Fullscreen) {
2860 ourWindow->SetFullScreen(true);
2861 } else if (sizeMode != nsSizeMode_Minimized) {
2862 if (ourWindow->GetFullScreen()) {
2863 // The first SetFullscreenInternal call below ensures that we do
2864 // not trigger any fullscreen transition even if the window was
2865 // put in fullscreen only for the Fullscreen API. The second
2866 // SetFullScreen call ensures that the window really exit from
2867 // fullscreen even if it entered fullscreen for both Fullscreen
2868 // Mode and Fullscreen API.
2869 ourWindow->SetFullscreenInternal(
2870 FullscreenReason::ForForceExitFullscreen, false);
2871 ourWindow->SetFullScreen(false);
2875 // And always fire a user-defined sizemodechange event on the window
2876 ourWindow->DispatchCustomEvent(u"sizemodechange"_ns);
2879 if (PresShell* presShell = GetPresShell()) {
2880 presShell->GetPresContext()->SizeModeChanged(sizeMode);
2883 // Note the current implementation of SetSizeMode just stores
2884 // the new state; it doesn't actually resize. So here we store
2885 // the state and pass the event on to the OS. The day is coming
2886 // when we'll handle the event here, and the return result will
2887 // then need to be different.
2890 void AppWindow::UIResolutionChanged() {
2891 nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
2892 mDocShell ? mDocShell->GetWindow() : nullptr;
2893 if (ourWindow) {
2894 ourWindow->DispatchCustomEvent(u"resolutionchange"_ns,
2895 ChromeOnlyDispatch::eYes);
2899 void AppWindow::FullscreenWillChange(bool aInFullscreen) {
2900 if (mDocShell) {
2901 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2902 ourWindow->FullscreenWillChange(aInFullscreen);
2905 MOZ_ASSERT(mFullscreenChangeState == FullscreenChangeState::NotChanging);
2906 mFullscreenChangeState = FullscreenChangeState::WillChange;
2909 void AppWindow::FullscreenChanged(bool aInFullscreen) {
2910 if (mFullscreenChangeState == FullscreenChangeState::WidgetResized) {
2911 FinishFullscreenChange(aInFullscreen);
2912 } else {
2913 NS_WARNING_ASSERTION(
2914 mFullscreenChangeState == FullscreenChangeState::WillChange,
2915 "Unexpected fullscreen change state");
2916 FullscreenChangeState newState =
2917 aInFullscreen ? FullscreenChangeState::WidgetEnteredFullscreen
2918 : FullscreenChangeState::WidgetExitedFullscreen;
2919 mFullscreenChangeState = newState;
2920 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
2921 // Wait for resize for a small amount of time.
2922 // 80ms is actually picked arbitrarily. But it shouldn't be too large
2923 // in case the widget resize is not going to happen at all, which can
2924 // be the case for some Linux window managers and possibly Android.
2925 NS_DelayedDispatchToCurrentThread(
2926 NS_NewRunnableFunction(
2927 "AppWindow::FullscreenChanged",
2928 [this, kungFuDeathGrip, newState, aInFullscreen]() {
2929 if (mFullscreenChangeState == newState) {
2930 FinishFullscreenChange(aInFullscreen);
2933 80);
2937 void AppWindow::FinishFullscreenChange(bool aInFullscreen) {
2938 mFullscreenChangeState = FullscreenChangeState::NotChanging;
2939 if (mDocShell) {
2940 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2941 ourWindow->FinishFullscreenChange(aInFullscreen);
2946 void AppWindow::MacFullscreenMenubarOverlapChanged(
2947 mozilla::DesktopCoord aOverlapAmount) {
2948 if (mDocShell) {
2949 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2950 ourWindow->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
2955 void AppWindow::OcclusionStateChanged(bool aIsFullyOccluded) {
2956 nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
2957 mDocShell ? mDocShell->GetWindow() : nullptr;
2958 if (ourWindow) {
2959 // And always fire a user-defined occlusionstatechange event on the window
2960 ourWindow->DispatchCustomEvent(u"occlusionstatechange"_ns,
2961 ChromeOnlyDispatch::eYes);
2965 void AppWindow::OSToolbarButtonPressed() {
2966 // Keep a reference as setting the chrome flags can fire events.
2967 nsCOMPtr<nsIAppWindow> appWindow(this);
2969 // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
2970 // due to components with multiple sidebar components
2971 // (such as Mail/News, Addressbook, etc)... and frankly,
2972 // Mac IE, OmniWeb, and other Mac OS X apps all work this way
2973 uint32_t chromeMask = (nsIWebBrowserChrome::CHROME_TOOLBAR |
2974 nsIWebBrowserChrome::CHROME_LOCATIONBAR |
2975 nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
2977 nsCOMPtr<nsIWebBrowserChrome> wbc(do_GetInterface(appWindow));
2978 if (!wbc) return;
2980 uint32_t chromeFlags, newChromeFlags = 0;
2981 wbc->GetChromeFlags(&chromeFlags);
2982 newChromeFlags = chromeFlags & chromeMask;
2983 if (!newChromeFlags)
2984 chromeFlags |= chromeMask;
2985 else
2986 chromeFlags &= (~newChromeFlags);
2987 wbc->SetChromeFlags(chromeFlags);
2990 bool AppWindow::ZLevelChanged(bool aImmediate, nsWindowZ* aPlacement,
2991 nsIWidget* aRequestBelow,
2992 nsIWidget** aActualBelow) {
2993 if (aActualBelow) *aActualBelow = nullptr;
2995 return ConstrainToZLevel(aImmediate, aPlacement, aRequestBelow, aActualBelow);
2998 void AppWindow::WindowActivated() {
2999 nsCOMPtr<nsIAppWindow> appWindow(this);
3001 // focusing the window could cause it to close, so keep a reference to it
3002 nsCOMPtr<nsPIDOMWindowOuter> window =
3003 mDocShell ? mDocShell->GetWindow() : nullptr;
3004 nsFocusManager* fm = nsFocusManager::GetFocusManager();
3005 if (fm && window) {
3006 fm->WindowRaised(window, nsFocusManager::GenerateFocusActionId());
3009 if (mChromeLoaded) {
3010 PersistentAttributesDirty(PAD_POSITION | PAD_SIZE | PAD_MISC);
3011 SavePersistentAttributes();
3015 void AppWindow::WindowDeactivated() {
3016 nsCOMPtr<nsIAppWindow> appWindow(this);
3018 nsCOMPtr<nsPIDOMWindowOuter> window =
3019 mDocShell ? mDocShell->GetWindow() : nullptr;
3020 nsFocusManager* fm = nsFocusManager::GetFocusManager();
3021 if (fm && window && !fm->IsTestMode()) {
3022 fm->WindowLowered(window, nsFocusManager::GenerateFocusActionId());
3026 #ifdef USE_NATIVE_MENUS
3027 static void LoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
3028 if (gfxPlatform::IsHeadless()) {
3029 return;
3032 // Find the menubar tag (if there is more than one, we ignore all but
3033 // the first).
3034 nsCOMPtr<nsINodeList> menubarElements = aDoc->GetElementsByTagNameNS(
3035 nsLiteralString(
3036 u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
3037 u"menubar"_ns);
3039 nsCOMPtr<nsINode> menubarNode;
3040 if (menubarElements) {
3041 menubarNode = menubarElements->Item(0);
3044 using widget::NativeMenuSupport;
3045 if (menubarNode) {
3046 nsCOMPtr<Element> menubarContent(do_QueryInterface(menubarNode));
3047 NativeMenuSupport::CreateNativeMenuBar(aParentWindow, menubarContent);
3048 } else {
3049 NativeMenuSupport::CreateNativeMenuBar(aParentWindow, nullptr);
3053 class L10nReadyPromiseHandler final : public dom::PromiseNativeHandler {
3054 public:
3055 NS_DECL_ISUPPORTS
3057 L10nReadyPromiseHandler(Document* aDoc, nsIWidget* aParentWindow)
3058 : mDocument(aDoc), mWindow(aParentWindow) {}
3060 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
3061 LoadNativeMenus(mDocument, mWindow);
3064 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
3065 // Again, this shouldn't happen, but fallback to loading the menus as is.
3066 NS_WARNING(
3067 "L10nReadyPromiseHandler rejected - loading fallback native "
3068 "menu.");
3069 LoadNativeMenus(mDocument, mWindow);
3072 private:
3073 ~L10nReadyPromiseHandler() = default;
3075 RefPtr<Document> mDocument;
3076 nsCOMPtr<nsIWidget> mWindow;
3079 NS_IMPL_ISUPPORTS0(L10nReadyPromiseHandler)
3081 #endif
3083 class AppWindowTimerCallback final : public nsITimerCallback, public nsINamed {
3084 public:
3085 explicit AppWindowTimerCallback(AppWindow* aWindow) : mWindow(aWindow) {}
3087 NS_DECL_THREADSAFE_ISUPPORTS
3089 NS_IMETHOD Notify(nsITimer* aTimer) override {
3090 // Although this object participates in a refcount cycle (this -> mWindow
3091 // -> mSPTimer -> this), mSPTimer is a one-shot timer and releases this
3092 // after it fires. So we don't need to release mWindow here.
3094 mWindow->FirePersistenceTimer();
3095 return NS_OK;
3098 NS_IMETHOD GetName(nsACString& aName) override {
3099 aName.AssignLiteral("AppWindowTimerCallback");
3100 return NS_OK;
3103 private:
3104 ~AppWindowTimerCallback() {}
3106 RefPtr<AppWindow> mWindow;
3109 NS_IMPL_ISUPPORTS(AppWindowTimerCallback, nsITimerCallback, nsINamed)
3111 void AppWindow::SetPersistenceTimer(uint32_t aDirtyFlags) {
3112 MutexAutoLock lock(mSPTimerLock);
3113 if (!mSPTimer) {
3114 mSPTimer = NS_NewTimer();
3115 if (!mSPTimer) {
3116 NS_WARNING("Couldn't create @mozilla.org/timer;1 instance?");
3117 return;
3121 RefPtr<AppWindowTimerCallback> callback = new AppWindowTimerCallback(this);
3122 mSPTimer->InitWithCallback(callback, SIZE_PERSISTENCE_TIMEOUT,
3123 nsITimer::TYPE_ONE_SHOT);
3125 PersistentAttributesDirty(aDirtyFlags);
3128 void AppWindow::FirePersistenceTimer() {
3129 MutexAutoLock lock(mSPTimerLock);
3130 SavePersistentAttributes();
3133 //----------------------------------------
3134 // nsIWebProgessListener implementation
3135 //----------------------------------------
3136 NS_IMETHODIMP
3137 AppWindow::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3138 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
3139 int32_t aCurTotalProgress,
3140 int32_t aMaxTotalProgress) {
3141 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3142 return NS_OK;
3145 NS_IMETHODIMP
3146 AppWindow::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3147 uint32_t aStateFlags, nsresult aStatus) {
3148 // If the notification is not about a document finishing, then just
3149 // ignore it...
3150 if (!(aStateFlags & nsIWebProgressListener::STATE_STOP) ||
3151 !(aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) {
3152 return NS_OK;
3155 if (mChromeLoaded) return NS_OK;
3157 // If this document notification is for a frame then ignore it...
3158 nsCOMPtr<mozIDOMWindowProxy> eventWin;
3159 aProgress->GetDOMWindow(getter_AddRefs(eventWin));
3160 auto* eventPWin = nsPIDOMWindowOuter::From(eventWin);
3161 if (eventPWin) {
3162 nsPIDOMWindowOuter* rootPWin = eventPWin->GetPrivateRoot();
3163 if (eventPWin != rootPWin) return NS_OK;
3166 mChromeLoaded = true;
3167 mLockedUntilChromeLoad = false;
3169 #ifdef USE_NATIVE_MENUS
3170 ///////////////////////////////
3171 // Find the Menubar DOM and Load the menus, hooking them up to the loaded
3172 // commands
3173 ///////////////////////////////
3174 nsCOMPtr<nsIContentViewer> cv;
3175 mDocShell->GetContentViewer(getter_AddRefs(cv));
3176 if (cv) {
3177 RefPtr<Document> menubarDoc = cv->GetDocument();
3178 if (menubarDoc) {
3179 RefPtr<DocumentL10n> l10n = menubarDoc->GetL10n();
3180 if (l10n) {
3181 // Wait for l10n to be ready so the menus are localized.
3182 RefPtr<Promise> promise = l10n->Ready();
3183 MOZ_ASSERT(promise);
3184 RefPtr<L10nReadyPromiseHandler> handler =
3185 new L10nReadyPromiseHandler(menubarDoc, mWindow);
3186 promise->AppendNativeHandler(handler);
3187 } else {
3188 // Something went wrong loading the doc and l10n wasn't created. This
3189 // shouldn't really happen, but if it does fallback to trying to load
3190 // the menus as is.
3191 LoadNativeMenus(menubarDoc, mWindow);
3195 #endif // USE_NATIVE_MENUS
3197 OnChromeLoaded();
3199 return NS_OK;
3202 NS_IMETHODIMP
3203 AppWindow::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3204 nsIURI* aURI, uint32_t aFlags) {
3205 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3206 return NS_OK;
3209 NS_IMETHODIMP
3210 AppWindow::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3211 nsresult aStatus, const char16_t* aMessage) {
3212 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3213 return NS_OK;
3216 NS_IMETHODIMP
3217 AppWindow::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3218 uint32_t aState) {
3219 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3220 return NS_OK;
3223 NS_IMETHODIMP
3224 AppWindow::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
3225 nsIRequest* aRequest, uint32_t aEvent) {
3226 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3227 return NS_OK;
3231 * ExecuteCloseHandler - Run the close handler, if any.
3232 * @return true iff we found a close handler to run.
3234 bool AppWindow::ExecuteCloseHandler() {
3235 /* If the event handler closes this window -- a likely scenario --
3236 things get deleted out of order without this death grip.
3237 (The problem may be the death grip in nsWindow::windowProc,
3238 which forces this window's widget to remain alive longer
3239 than it otherwise would.) */
3240 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
3242 nsCOMPtr<EventTarget> eventTarget;
3243 if (mDocShell) {
3244 eventTarget = do_QueryInterface(mDocShell->GetWindow());
3247 if (eventTarget) {
3248 nsCOMPtr<nsIContentViewer> contentViewer;
3249 mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
3250 if (contentViewer) {
3251 RefPtr<nsPresContext> presContext = contentViewer->GetPresContext();
3253 nsEventStatus status = nsEventStatus_eIgnore;
3254 WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
3256 nsresult rv = EventDispatcher::Dispatch(eventTarget, presContext, &event,
3257 nullptr, &status);
3258 if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault)
3259 return true;
3260 // else fall through and return false
3264 return false;
3265 } // ExecuteCloseHandler
3267 void AppWindow::ConstrainToOpenerScreen(int32_t* aX, int32_t* aY) {
3268 if (mOpenerScreenRect.IsEmpty()) {
3269 *aX = *aY = 0;
3270 return;
3273 int32_t left, top, width, height;
3274 // Constrain initial positions to the same screen as opener
3275 nsCOMPtr<nsIScreenManager> screenmgr =
3276 do_GetService("@mozilla.org/gfx/screenmanager;1");
3277 if (screenmgr) {
3278 nsCOMPtr<nsIScreen> screen;
3279 screenmgr->ScreenForRect(
3280 mOpenerScreenRect.X(), mOpenerScreenRect.Y(), mOpenerScreenRect.Width(),
3281 mOpenerScreenRect.Height(), getter_AddRefs(screen));
3282 if (screen) {
3283 screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
3284 if (*aX < left || *aX > left + width) {
3285 *aX = left;
3287 if (*aY < top || *aY > top + height) {
3288 *aY = top;
3294 nsIAppWindow* AppWindow::WidgetListenerDelegate::GetAppWindow() {
3295 return mAppWindow->GetAppWindow();
3298 PresShell* AppWindow::WidgetListenerDelegate::GetPresShell() {
3299 return mAppWindow->GetPresShell();
3302 bool AppWindow::WidgetListenerDelegate::WindowMoved(nsIWidget* aWidget,
3303 int32_t aX, int32_t aY) {
3304 RefPtr<AppWindow> holder = mAppWindow;
3305 return holder->WindowMoved(aWidget, aX, aY);
3308 bool AppWindow::WidgetListenerDelegate::WindowResized(nsIWidget* aWidget,
3309 int32_t aWidth,
3310 int32_t aHeight) {
3311 RefPtr<AppWindow> holder = mAppWindow;
3312 return holder->WindowResized(aWidget, aWidth, aHeight);
3315 bool AppWindow::WidgetListenerDelegate::RequestWindowClose(nsIWidget* aWidget) {
3316 RefPtr<AppWindow> holder = mAppWindow;
3317 return holder->RequestWindowClose(aWidget);
3320 void AppWindow::WidgetListenerDelegate::SizeModeChanged(nsSizeMode aSizeMode) {
3321 RefPtr<AppWindow> holder = mAppWindow;
3322 holder->SizeModeChanged(aSizeMode);
3325 void AppWindow::WidgetListenerDelegate::UIResolutionChanged() {
3326 RefPtr<AppWindow> holder = mAppWindow;
3327 holder->UIResolutionChanged();
3330 void AppWindow::WidgetListenerDelegate::FullscreenWillChange(
3331 bool aInFullscreen) {
3332 RefPtr<AppWindow> holder = mAppWindow;
3333 holder->FullscreenWillChange(aInFullscreen);
3336 void AppWindow::WidgetListenerDelegate::FullscreenChanged(bool aInFullscreen) {
3337 RefPtr<AppWindow> holder = mAppWindow;
3338 holder->FullscreenChanged(aInFullscreen);
3341 void AppWindow::WidgetListenerDelegate::MacFullscreenMenubarOverlapChanged(
3342 DesktopCoord aOverlapAmount) {
3343 RefPtr<AppWindow> holder = mAppWindow;
3344 return holder->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
3347 void AppWindow::WidgetListenerDelegate::OcclusionStateChanged(
3348 bool aIsFullyOccluded) {
3349 RefPtr<AppWindow> holder = mAppWindow;
3350 holder->OcclusionStateChanged(aIsFullyOccluded);
3353 void AppWindow::WidgetListenerDelegate::OSToolbarButtonPressed() {
3354 RefPtr<AppWindow> holder = mAppWindow;
3355 holder->OSToolbarButtonPressed();
3358 bool AppWindow::WidgetListenerDelegate::ZLevelChanged(
3359 bool aImmediate, nsWindowZ* aPlacement, nsIWidget* aRequestBelow,
3360 nsIWidget** aActualBelow) {
3361 RefPtr<AppWindow> holder = mAppWindow;
3362 return holder->ZLevelChanged(aImmediate, aPlacement, aRequestBelow,
3363 aActualBelow);
3366 void AppWindow::WidgetListenerDelegate::WindowActivated() {
3367 RefPtr<AppWindow> holder = mAppWindow;
3368 holder->WindowActivated();
3371 void AppWindow::WidgetListenerDelegate::WindowDeactivated() {
3372 RefPtr<AppWindow> holder = mAppWindow;
3373 holder->WindowDeactivated();
3376 } // namespace mozilla