Bug 1866777 - Disable test_race_cache_with_network.js on windows opt for frequent...
[gecko.git] / xpfe / appshell / AppWindow.cpp
blobe03e6540160de8d16a42de60603760bd8df433c7
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 ci et: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ErrorList.h"
8 #include "mozilla/MathAlgorithms.h"
10 // Local includes
11 #include "AppWindow.h"
12 #include <algorithm>
14 // Helper classes
15 #include "nsPrintfCString.h"
16 #include "nsString.h"
17 #include "nsWidgetsCID.h"
18 #include "nsThreadUtils.h"
19 #include "nsNetCID.h"
20 #include "nsQueryObject.h"
21 #include "mozilla/ProfilerLabels.h"
22 #include "mozilla/Sprintf.h"
23 #include "mozilla/Try.h"
25 // Interfaces needed to be included
26 #include "nsGlobalWindowOuter.h"
27 #include "nsIAppShell.h"
28 #include "nsIAppShellService.h"
29 #include "nsIDocumentViewer.h"
30 #include "mozilla/dom/Document.h"
31 #include "mozilla/dom/CanonicalBrowsingContext.h"
32 #include "nsPIDOMWindow.h"
33 #include "nsScreen.h"
34 #include "nsIInterfaceRequestor.h"
35 #include "nsIInterfaceRequestorUtils.h"
36 #include "nsIIOService.h"
37 #include "nsIObserverService.h"
38 #include "nsIOpenWindowInfo.h"
39 #include "nsIWindowMediator.h"
40 #include "nsIScreenManager.h"
41 #include "nsIScreen.h"
42 #include "nsIWindowWatcher.h"
43 #include "nsIURI.h"
44 #include "nsAppShellCID.h"
45 #include "nsReadableUtils.h"
46 #include "nsStyleConsts.h"
47 #include "nsPresContext.h"
48 #include "nsContentUtils.h"
49 #include "nsXULTooltipListener.h"
50 #include "nsXULPopupManager.h"
51 #include "nsFocusManager.h"
52 #include "nsContentList.h"
53 #include "nsIDOMWindowUtils.h"
54 #include "nsServiceManagerUtils.h"
56 #include "prenv.h"
57 #include "mozilla/AppShutdown.h"
58 #include "mozilla/AutoRestore.h"
59 #include "mozilla/Preferences.h"
60 #include "mozilla/PresShell.h"
61 #include "mozilla/Services.h"
62 #include "mozilla/SpinEventLoopUntil.h"
63 #include "mozilla/dom/BarProps.h"
64 #include "mozilla/dom/DOMRect.h"
65 #include "mozilla/dom/Element.h"
66 #include "mozilla/dom/Event.h"
67 #include "mozilla/dom/ScriptSettings.h"
68 #include "mozilla/dom/BrowserHost.h"
69 #include "mozilla/dom/BrowserParent.h"
70 #include "mozilla/dom/LoadURIOptionsBinding.h"
71 #include "mozilla/intl/LocaleService.h"
72 #include "mozilla/EventDispatcher.h"
74 #ifdef XP_WIN
75 # include "mozilla/PreXULSkeletonUI.h"
76 # include "nsIWindowsUIUtils.h"
77 #endif
79 #include "mozilla/dom/DocumentL10n.h"
81 #ifdef XP_MACOSX
82 # include "mozilla/widget/NativeMenuSupport.h"
83 # define USE_NATIVE_MENUS
84 #endif
86 #define SIZEMODE_NORMAL u"normal"_ns
87 #define SIZEMODE_MAXIMIZED u"maximized"_ns
88 #define SIZEMODE_MINIMIZED u"minimized"_ns
89 #define SIZEMODE_FULLSCREEN u"fullscreen"_ns
91 #define SIZE_PERSISTENCE_TIMEOUT 500 // msec
93 //*****************************************************************************
94 //*** AppWindow: Object Management
95 //*****************************************************************************
97 namespace mozilla {
99 using dom::AutoNoJSAPI;
100 using dom::BrowserHost;
101 using dom::BrowsingContext;
102 using dom::Document;
103 using dom::DocumentL10n;
104 using dom::Element;
105 using dom::EventTarget;
106 using dom::LoadURIOptions;
107 using dom::Promise;
109 AppWindow::AppWindow(uint32_t aChromeFlags)
110 : mChromeTreeOwner(nullptr),
111 mContentTreeOwner(nullptr),
112 mPrimaryContentTreeOwner(nullptr),
113 mModalStatus(NS_OK),
114 mFullscreenChangeState(FullscreenChangeState::NotChanging),
115 mContinueModalLoop(false),
116 mDebuting(false),
117 mChromeLoaded(false),
118 mSizingShellFromXUL(false),
119 mShowAfterLoad(false),
120 mIntrinsicallySized(false),
121 mCenterAfterLoad(false),
122 mIsHiddenWindow(false),
123 mLockedUntilChromeLoad(false),
124 mIgnoreXULSize(false),
125 mIgnoreXULPosition(false),
126 mChromeFlagsFrozen(false),
127 mIgnoreXULSizeMode(false),
128 mDestroying(false),
129 mRegistered(false),
130 mDominantClientSize(false),
131 mChromeFlags(aChromeFlags),
132 mWidgetListenerDelegate(this) {}
134 AppWindow::~AppWindow() {
135 if (mSPTimer) {
136 mSPTimer->Cancel();
137 mSPTimer = nullptr;
139 Destroy();
142 //*****************************************************************************
143 // AppWindow::nsISupports
144 //*****************************************************************************
146 NS_IMPL_ADDREF(AppWindow)
147 NS_IMPL_RELEASE(AppWindow)
149 NS_INTERFACE_MAP_BEGIN(AppWindow)
150 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAppWindow)
151 NS_INTERFACE_MAP_ENTRY(nsIAppWindow)
152 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
153 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
154 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
155 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
156 NS_INTERFACE_MAP_ENTRY_CONCRETE(AppWindow)
157 NS_INTERFACE_MAP_END
159 nsresult AppWindow::Initialize(nsIAppWindow* aParent, nsIAppWindow* aOpener,
160 int32_t aInitialWidth, int32_t aInitialHeight,
161 bool aIsHiddenWindow,
162 widget::InitData& widgetInitData) {
163 nsresult rv;
164 nsCOMPtr<nsIWidget> parentWidget;
166 mIsHiddenWindow = aIsHiddenWindow;
168 DesktopIntPoint initialPos;
169 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aOpener));
170 if (base) {
171 LayoutDeviceIntRect rect = base->GetPositionAndSize();
172 mOpenerScreenRect =
173 DesktopIntRect::Round(rect / base->DevicePixelsPerDesktopPixel());
174 if (!mOpenerScreenRect.IsEmpty()) {
175 initialPos = mOpenerScreenRect.TopLeft();
176 ConstrainToOpenerScreen(&initialPos.x.value, &initialPos.y.value);
180 // XXX: need to get the default window size from prefs...
181 // Doesn't come from prefs... will come from CSS/XUL/RDF
182 DesktopIntRect deskRect(initialPos,
183 DesktopIntSize(aInitialWidth, aInitialHeight));
185 // Create top level window
186 if (gfxPlatform::IsHeadless()) {
187 mWindow = nsIWidget::CreateHeadlessWidget();
188 } else {
189 mWindow = nsIWidget::CreateTopLevelWindow();
191 if (!mWindow) {
192 return NS_ERROR_FAILURE;
195 /* This next bit is troublesome. We carry two different versions of a pointer
196 to our parent window. One is the parent window's widget, which is passed
197 to our own widget. The other is a weak reference we keep here to our
198 parent AppWindow. The former is useful to the widget, and we can't
199 trust its treatment of the parent reference because they're platform-
200 specific. The latter is useful to this class.
201 A better implementation would be one in which the parent keeps strong
202 references to its children and closes them before it allows itself
203 to be closed. This would mimic the behaviour of OSes that support
204 top-level child windows in OSes that do not. Later.
206 nsCOMPtr<nsIBaseWindow> parentAsWin(do_QueryInterface(aParent));
207 if (parentAsWin) {
208 parentAsWin->GetMainWidget(getter_AddRefs(parentWidget));
209 mParentWindow = do_GetWeakReference(aParent);
212 mWindow->SetWidgetListener(&mWidgetListenerDelegate);
213 rv = mWindow->Create((nsIWidget*)parentWidget, // Parent nsIWidget
214 nullptr, // Native parent widget
215 deskRect, // Widget dimensions
216 &widgetInitData); // Widget initialization data
217 NS_ENSURE_SUCCESS(rv, rv);
219 LayoutDeviceIntRect r = mWindow->GetClientBounds();
220 // Match the default background color of content. Important on windows
221 // since we no longer use content child widgets.
222 mWindow->SetBackgroundColor(NS_RGB(255, 255, 255));
224 // All Chrome BCs exist within the same BrowsingContextGroup, so we don't need
225 // to pass in the opener window here. The opener is set later, if needed, by
226 // nsWindowWatcher.
227 RefPtr<BrowsingContext> browsingContext =
228 BrowsingContext::CreateIndependent(BrowsingContext::Type::Chrome);
230 // Create web shell
231 mDocShell = nsDocShell::Create(browsingContext);
232 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
234 // Make sure to set the item type on the docshell _before_ calling
235 // InitWindow() so it knows what type it is.
236 NS_ENSURE_SUCCESS(EnsureChromeTreeOwner(), NS_ERROR_FAILURE);
238 mDocShell->SetTreeOwner(mChromeTreeOwner);
240 r.MoveTo(0, 0);
241 NS_ENSURE_SUCCESS(mDocShell->InitWindow(nullptr, mWindow, r.X(), r.Y(),
242 r.Width(), r.Height()),
243 NS_ERROR_FAILURE);
245 // Attach a WebProgress listener.during initialization...
246 mDocShell->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK);
248 mWindow->MaybeDispatchInitialFocusEvent();
250 return rv;
253 //*****************************************************************************
254 // AppWindow::nsIIntefaceRequestor
255 //*****************************************************************************
257 NS_IMETHODIMP AppWindow::GetInterface(const nsIID& aIID, void** aSink) {
258 nsresult rv;
260 NS_ENSURE_ARG_POINTER(aSink);
262 if (aIID.Equals(NS_GET_IID(nsIPrompt))) {
263 rv = EnsurePrompter();
264 if (NS_FAILED(rv)) return rv;
265 return mPrompter->QueryInterface(aIID, aSink);
267 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
268 rv = EnsureAuthPrompter();
269 if (NS_FAILED(rv)) return rv;
270 return mAuthPrompter->QueryInterface(aIID, aSink);
272 if (aIID.Equals(NS_GET_IID(mozIDOMWindowProxy))) {
273 return GetWindowDOMWindow(reinterpret_cast<mozIDOMWindowProxy**>(aSink));
275 if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) {
276 nsCOMPtr<mozIDOMWindowProxy> window = nullptr;
277 rv = GetWindowDOMWindow(getter_AddRefs(window));
278 nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(window);
279 domWindow.forget(aSink);
280 return rv;
282 if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome)) &&
283 NS_SUCCEEDED(EnsureContentTreeOwner()) &&
284 NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink))) {
285 return NS_OK;
288 return QueryInterface(aIID, aSink);
291 //*****************************************************************************
292 // AppWindow::nsIAppWindow
293 //*****************************************************************************
295 NS_IMETHODIMP AppWindow::GetDocShell(nsIDocShell** aDocShell) {
296 NS_ENSURE_ARG_POINTER(aDocShell);
298 *aDocShell = mDocShell;
299 NS_IF_ADDREF(*aDocShell);
300 return NS_OK;
303 NS_IMETHODIMP AppWindow::GetZLevel(uint32_t* outLevel) {
304 nsCOMPtr<nsIWindowMediator> mediator(
305 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
306 if (mediator)
307 mediator->GetZLevel(this, outLevel);
308 else
309 *outLevel = normalZ;
310 return NS_OK;
313 NS_IMETHODIMP AppWindow::SetZLevel(uint32_t aLevel) {
314 nsCOMPtr<nsIWindowMediator> mediator(
315 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
316 if (!mediator) return NS_ERROR_FAILURE;
318 uint32_t zLevel;
319 mediator->GetZLevel(this, &zLevel);
320 if (zLevel == aLevel) return NS_OK;
322 /* refuse to raise a maximized window above the normal browser level,
323 for fear it could hide newly opened browser windows */
324 if (aLevel > nsIAppWindow::normalZ && mWindow) {
325 nsSizeMode sizeMode = mWindow->SizeMode();
326 if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) {
327 return NS_ERROR_FAILURE;
331 // do it
332 mediator->SetZLevel(this, aLevel);
333 PersistentAttributesDirty(PersistentAttribute::Misc, Sync);
335 nsCOMPtr<nsIDocumentViewer> viewer;
336 mDocShell->GetDocViewer(getter_AddRefs(viewer));
337 if (viewer) {
338 RefPtr<dom::Document> doc = viewer->GetDocument();
339 if (doc) {
340 ErrorResult rv;
341 RefPtr<dom::Event> event =
342 doc->CreateEvent(u"Events"_ns, dom::CallerType::System, rv);
343 if (event) {
344 event->InitEvent(u"windowZLevel"_ns, true, false);
346 event->SetTrusted(true);
348 doc->DispatchEvent(*event);
352 return NS_OK;
355 NS_IMETHODIMP AppWindow::GetChromeFlags(uint32_t* aChromeFlags) {
356 NS_ENSURE_ARG_POINTER(aChromeFlags);
357 *aChromeFlags = mChromeFlags;
358 return NS_OK;
361 NS_IMETHODIMP AppWindow::SetChromeFlags(uint32_t aChromeFlags) {
362 NS_ASSERTION(!mChromeFlagsFrozen,
363 "SetChromeFlags() after AssumeChromeFlagsAreFrozen()!");
365 mChromeFlags = aChromeFlags;
366 if (mChromeLoaded) {
367 ApplyChromeFlags();
369 return NS_OK;
372 NS_IMETHODIMP AppWindow::AssumeChromeFlagsAreFrozen() {
373 mChromeFlagsFrozen = true;
374 return NS_OK;
377 NS_IMETHODIMP AppWindow::SetIntrinsicallySized(bool aIntrinsicallySized) {
378 mIntrinsicallySized = aIntrinsicallySized;
379 return NS_OK;
382 NS_IMETHODIMP AppWindow::GetIntrinsicallySized(bool* aIntrinsicallySized) {
383 NS_ENSURE_ARG_POINTER(aIntrinsicallySized);
385 *aIntrinsicallySized = mIntrinsicallySized;
386 return NS_OK;
389 NS_IMETHODIMP AppWindow::GetPrimaryContentShell(
390 nsIDocShellTreeItem** aDocShellTreeItem) {
391 NS_ENSURE_ARG_POINTER(aDocShellTreeItem);
392 NS_IF_ADDREF(*aDocShellTreeItem = mPrimaryContentShell);
393 return NS_OK;
396 NS_IMETHODIMP
397 AppWindow::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
398 if (aPrimary) {
399 mPrimaryBrowserParent = aTab;
400 mPrimaryContentShell = nullptr;
401 } else if (mPrimaryBrowserParent == aTab) {
402 mPrimaryBrowserParent = nullptr;
405 return NS_OK;
408 NS_IMETHODIMP
409 AppWindow::RemoteTabRemoved(nsIRemoteTab* aTab) {
410 if (aTab == mPrimaryBrowserParent) {
411 mPrimaryBrowserParent = nullptr;
414 return NS_OK;
417 NS_IMETHODIMP
418 AppWindow::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
419 nsCOMPtr<nsIRemoteTab> tab = mPrimaryBrowserParent;
420 tab.forget(aTab);
421 return NS_OK;
424 NS_IMETHODIMP
425 AppWindow::GetPrimaryContentBrowsingContext(
426 mozilla::dom::BrowsingContext** aBc) {
427 if (mPrimaryBrowserParent) {
428 return mPrimaryBrowserParent->GetBrowsingContext(aBc);
430 if (mPrimaryContentShell) {
431 return mPrimaryContentShell->GetBrowsingContextXPCOM(aBc);
433 *aBc = nullptr;
434 return NS_OK;
437 static LayoutDeviceIntSize GetOuterToInnerSizeDifference(nsIWidget* aWindow) {
438 if (!aWindow) {
439 return LayoutDeviceIntSize();
441 return aWindow->ClientToWindowSizeDifference();
444 static CSSIntSize GetOuterToInnerSizeDifferenceInCSSPixels(
445 nsIWidget* aWindow, CSSToLayoutDeviceScale aScale) {
446 LayoutDeviceIntSize devPixelSize = GetOuterToInnerSizeDifference(aWindow);
447 return RoundedToInt(devPixelSize / aScale);
450 NS_IMETHODIMP
451 AppWindow::GetOuterToInnerHeightDifferenceInCSSPixels(uint32_t* aResult) {
452 *aResult = GetOuterToInnerSizeDifferenceInCSSPixels(
453 mWindow, UnscaledDevicePixelsPerCSSPixel())
454 .height;
455 return NS_OK;
458 NS_IMETHODIMP
459 AppWindow::GetOuterToInnerWidthDifferenceInCSSPixels(uint32_t* aResult) {
460 *aResult = GetOuterToInnerSizeDifferenceInCSSPixels(
461 mWindow, UnscaledDevicePixelsPerCSSPixel())
462 .width;
463 return NS_OK;
466 nsTArray<RefPtr<mozilla::LiveResizeListener>>
467 AppWindow::GetLiveResizeListeners() {
468 nsTArray<RefPtr<mozilla::LiveResizeListener>> listeners;
469 if (mPrimaryBrowserParent) {
470 BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
471 RefPtr<mozilla::LiveResizeListener> actor = host->GetActor();
472 if (actor) {
473 listeners.AppendElement(actor);
476 return listeners;
479 NS_IMETHODIMP AppWindow::ShowModal() {
480 AUTO_PROFILER_LABEL("AppWindow::ShowModal", OTHER);
482 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
483 MOZ_ASSERT_UNREACHABLE(
484 "Trying to show modal window after shutdown started.");
485 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
488 // Store locally so it doesn't die on us
489 nsCOMPtr<nsIWidget> window = mWindow;
490 nsCOMPtr<nsIAppWindow> tempRef = this;
492 #ifdef USE_NATIVE_MENUS
493 // macOS only: For modals created early in startup.
494 // (e.g. ProfileManager/ProfileDowngrade) this creates a fallback menu for
495 // the menu bar which only contains a "Quit" menu item.
496 // This allows the user to quit the application in a regular way with cmd+Q.
497 widget::NativeMenuSupport::CreateNativeMenuBar(mWindow, nullptr);
498 #endif
500 window->SetModal(true);
501 mContinueModalLoop = true;
502 EnableParent(false);
505 AutoNoJSAPI nojsapi;
506 SpinEventLoopUntil("AppWindow::ShowModal"_ns, [&]() {
507 if (MOZ_UNLIKELY(
508 AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed))) {
509 // TODO: Bug 1699041 would apply also here: Should we return an error
510 // if we are bailing out from a pre-existing modal dialog for shutdown?
511 ExitModalLoop(NS_OK);
513 return !mContinueModalLoop;
517 mContinueModalLoop = false;
518 window->SetModal(false);
519 /* Note there's no EnableParent(true) here to match the false one
520 above. That's done in ExitModalLoop. It's important that the parent
521 be re-enabled before this window is made invisible; to do otherwise
522 causes bizarre z-ordering problems. At this point, the window is
523 already invisible.
524 No known current implementation of Enable would have a problem with
525 re-enabling the parent twice, so we could do it again here without
526 breaking any current implementation. But that's unnecessary if the
527 modal loop is always exited using ExitModalLoop (the other way would be
528 to change the protected member variable directly.)
531 return mModalStatus;
534 //*****************************************************************************
535 // AppWindow::nsIBaseWindow
536 //*****************************************************************************
538 NS_IMETHODIMP AppWindow::InitWindow(nativeWindow aParentNativeWindow,
539 nsIWidget* parentWidget, int32_t x,
540 int32_t y, int32_t cx, int32_t cy) {
541 // XXX First Check In
542 NS_ASSERTION(false, "Not Yet Implemented");
543 return NS_OK;
546 NS_IMETHODIMP AppWindow::Destroy() {
547 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
549 if (mDocShell) {
550 mDocShell->RemoveProgressListener(this);
553 if (mSPTimer) {
554 mSPTimer->Cancel();
555 SavePersistentAttributes();
556 mSPTimer = nullptr;
559 if (!mWindow) return NS_OK;
561 // Ensure we don't reenter this code
562 if (mDestroying) return NS_OK;
564 mozilla::AutoRestore<bool> guard(mDestroying);
565 mDestroying = true;
567 nsCOMPtr<nsIAppShellService> appShell(
568 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
569 NS_ASSERTION(appShell, "Couldn't get appShell... xpcom shutdown?");
570 if (appShell) {
571 appShell->UnregisterTopLevelWindow(static_cast<nsIAppWindow*>(this));
574 // Remove modality (if any) and hide while destroying. More than
575 // a convenience, the hide prevents user interaction with the partially
576 // destroyed window. This is especially necessary when the eldest window
577 // in a stack of modal windows is destroyed first. It happens.
578 ExitModalLoop(NS_OK);
579 // XXX: Skip unmapping the window on Linux due to GLX hangs on the compositor
580 // thread with NVIDIA driver 310.32. We don't need to worry about user
581 // interactions with destroyed windows on X11 either.
582 #ifndef MOZ_WIDGET_GTK
583 if (mWindow) mWindow->Show(false);
584 #endif
586 #if defined(XP_WIN)
587 // We need to explicitly set the focus on Windows, but
588 // only if the parent is visible.
589 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
590 if (parent) {
591 nsCOMPtr<nsIWidget> parentWidget;
592 parent->GetMainWidget(getter_AddRefs(parentWidget));
594 if (parentWidget && parentWidget->IsVisible()) {
595 bool isParentHiddenWindow = false;
597 if (appShell) {
598 bool hasHiddenWindow = false;
599 appShell->GetHasHiddenWindow(&hasHiddenWindow);
600 if (hasHiddenWindow) {
601 nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
602 nsCOMPtr<nsIAppWindow> hiddenWindow;
603 appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
604 if (hiddenWindow) {
605 baseHiddenWindow = do_GetInterface(hiddenWindow);
606 isParentHiddenWindow = (baseHiddenWindow == parent);
611 // somebody screwed up somewhere. hiddenwindow shouldn't be anybody's
612 // parent. still, when it happens, skip activating it.
613 if (!isParentHiddenWindow) {
614 parentWidget->PlaceBehind(eZPlacementTop, 0, true);
618 #endif
620 RemoveTooltipSupport();
622 mDOMWindow = nullptr;
623 if (mDocShell) {
624 RefPtr<BrowsingContext> bc(mDocShell->GetBrowsingContext());
625 mDocShell->Destroy();
626 bc->Detach();
627 mDocShell = nullptr; // this can cause reentrancy of this function
630 mPrimaryContentShell = nullptr;
632 if (mContentTreeOwner) {
633 mContentTreeOwner->AppWindow(nullptr);
634 NS_RELEASE(mContentTreeOwner);
636 if (mPrimaryContentTreeOwner) {
637 mPrimaryContentTreeOwner->AppWindow(nullptr);
638 NS_RELEASE(mPrimaryContentTreeOwner);
640 if (mChromeTreeOwner) {
641 mChromeTreeOwner->AppWindow(nullptr);
642 NS_RELEASE(mChromeTreeOwner);
644 if (mWindow) {
645 mWindow->SetWidgetListener(nullptr); // nsWebShellWindow hackery
646 mWindow->Destroy();
647 mWindow = nullptr;
650 if (!mIsHiddenWindow && mRegistered) {
651 /* Inform appstartup we've destroyed this window and it could
652 quit now if it wanted. This must happen at least after mDocShell
653 is destroyed, because onunload handlers fire then, and those being
654 script, anything could happen. A new window could open, even.
655 See bug 130719. */
656 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
657 NS_ASSERTION(obssvc, "Couldn't get observer service?");
659 if (obssvc)
660 obssvc->NotifyObservers(nullptr, "xul-window-destroyed", nullptr);
663 return NS_OK;
666 NS_IMETHODIMP AppWindow::GetDevicePixelsPerDesktopPixel(double* aScale) {
667 *aScale = mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0;
668 return NS_OK;
671 double AppWindow::GetWidgetCSSToDeviceScale() {
672 return mWindow ? mWindow->GetDefaultScale().scale : 1.0;
675 NS_IMETHODIMP AppWindow::SetPositionDesktopPix(int32_t aX, int32_t aY) {
676 return MoveResize(Some(DesktopIntPoint(aX, aY)), Nothing(), false);
679 // The parameters here are device pixels; do the best we can to convert to
680 // desktop px, using the window's current scale factor (if available).
681 NS_IMETHODIMP AppWindow::SetPosition(int32_t aX, int32_t aY) {
682 // Don't reset the window's size mode here - platforms that don't want to move
683 // maximized windows should reset it in their respective Move implementation.
684 return MoveResize(Some(LayoutDeviceIntPoint(aX, aY)), Nothing(), false);
687 NS_IMETHODIMP AppWindow::GetPosition(int32_t* aX, int32_t* aY) {
688 return GetPositionAndSize(aX, aY, nullptr, nullptr);
691 NS_IMETHODIMP AppWindow::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) {
692 /* any attempt to set the window's size or position overrides the window's
693 zoom state. this is important when these two states are competing while
694 the window is being opened. but it should probably just always be so. */
695 return MoveResize(Nothing(), Some(LayoutDeviceIntSize(aCX, aCY)), aRepaint);
698 NS_IMETHODIMP AppWindow::GetSize(int32_t* aCX, int32_t* aCY) {
699 return GetPositionAndSize(nullptr, nullptr, aCX, aCY);
702 NS_IMETHODIMP AppWindow::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
703 int32_t aCY, uint32_t aFlags) {
704 /* any attempt to set the window's size or position overrides the window's
705 zoom state. this is important when these two states are competing while
706 the window is being opened. but it should probably just always be so. */
707 return MoveResize(Some(LayoutDeviceIntPoint(aX, aY)),
708 Some(LayoutDeviceIntSize(aCX, aCY)),
709 !!(aFlags & nsIBaseWindow::eRepaint));
712 NS_IMETHODIMP AppWindow::GetPositionAndSize(int32_t* x, int32_t* y, int32_t* cx,
713 int32_t* cy) {
714 if (!mWindow) return NS_ERROR_FAILURE;
716 LayoutDeviceIntRect rect = mWindow->GetScreenBounds();
718 if (x) *x = rect.X();
719 if (y) *y = rect.Y();
720 if (cx) *cx = rect.Width();
721 if (cy) *cy = rect.Height();
723 return NS_OK;
726 NS_IMETHODIMP
727 AppWindow::SetDimensions(DimensionRequest&& aRequest) {
728 if (aRequest.mDimensionKind == DimensionKind::Inner) {
729 // For the chrome the inner size is the root shell size, and for the
730 // content it's the primary content size. We lack an indicator here that
731 // would allow us to distinguish between the two.
732 return NS_ERROR_NOT_IMPLEMENTED;
735 MOZ_TRY(aRequest.SupplementFrom(this));
736 return aRequest.ApplyOuterTo(this);
739 NS_IMETHODIMP
740 AppWindow::GetDimensions(DimensionKind aDimensionKind, int32_t* aX, int32_t* aY,
741 int32_t* aCX, int32_t* aCY) {
742 if (aDimensionKind == DimensionKind::Inner) {
743 // For the chrome the inner size is the root shell size, and for the
744 // content it's the primary content size. We lack an indicator here that
745 // would allow us to distinguish between the two.
746 return NS_ERROR_NOT_IMPLEMENTED;
748 return GetPositionAndSize(aX, aY, aCX, aCY);
751 nsresult AppWindow::MoveResize(const Maybe<LayoutDeviceIntPoint>& aPosition,
752 const Maybe<LayoutDeviceIntSize>& aSize,
753 bool aRepaint) {
754 DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
756 return MoveResize(aPosition ? Some(*aPosition / scale) : Nothing(),
757 aSize ? Some(*aSize / scale) : Nothing(), aRepaint);
760 nsresult AppWindow::MoveResize(const Maybe<DesktopPoint>& aPosition,
761 const Maybe<DesktopSize>& aSize, bool aRepaint) {
762 NS_ENSURE_STATE(mWindow);
763 PersistentAttributes dirtyAttributes;
765 if (!aPosition && !aSize) {
766 MOZ_ASSERT_UNREACHABLE("Doing nothing?");
767 return NS_ERROR_UNEXPECTED;
770 if (aSize) {
771 mWindow->SetSizeMode(nsSizeMode_Normal);
772 mIntrinsicallySized = false;
773 mDominantClientSize = false;
776 if (aPosition && aSize) {
777 mWindow->Resize(aPosition->x, aPosition->y, aSize->width, aSize->height,
778 aRepaint);
779 dirtyAttributes = {PersistentAttribute::Size,
780 PersistentAttribute::Position};
781 } else if (aSize) {
782 mWindow->Resize(aSize->width, aSize->height, aRepaint);
783 dirtyAttributes = {PersistentAttribute::Size};
784 } else if (aPosition) {
785 mWindow->Move(aPosition->x, aPosition->y);
786 dirtyAttributes = {PersistentAttribute::Position};
789 if (mSizingShellFromXUL) {
790 // If we're invoked for sizing from XUL, we want to neither ignore anything
791 // nor persist anything, since it's already the value in XUL.
792 return NS_OK;
794 if (!mChromeLoaded) {
795 // If we're called before the chrome is loaded someone obviously wants this
796 // window at this size & in the normal size mode (since it is the only mode
797 // in which setting dimensions makes sense). We don't persist this one-time
798 // position/size.
799 if (aPosition) {
800 mIgnoreXULPosition = true;
802 if (aSize) {
803 mIgnoreXULSize = true;
804 mIgnoreXULSizeMode = true;
806 return NS_OK;
809 PersistentAttributesDirty(dirtyAttributes, Sync);
810 return NS_OK;
813 NS_IMETHODIMP AppWindow::Center(nsIAppWindow* aRelative, bool aScreen,
814 bool aAlert) {
815 DesktopIntRect rect;
816 bool screenCoordinates = false, windowCoordinates = false;
817 nsresult result;
819 if (!mChromeLoaded) {
820 // note we lose the parameters. at time of writing, this isn't a problem.
821 mCenterAfterLoad = true;
822 return NS_OK;
825 if (!aScreen && !aRelative) return NS_ERROR_INVALID_ARG;
827 nsCOMPtr<nsIScreenManager> screenmgr =
828 do_GetService("@mozilla.org/gfx/screenmanager;1", &result);
829 if (NS_FAILED(result)) {
830 return result;
833 nsCOMPtr<nsIScreen> screen;
835 if (aRelative) {
836 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aRelative));
837 if (base) {
838 rect = RoundedToInt(base->GetPositionAndSize() /
839 base->DevicePixelsPerDesktopPixel());
840 // if centering on screen, convert that to the corresponding screen
841 if (aScreen) {
842 screen = screenmgr->ScreenForRect(rect);
843 } else {
844 windowCoordinates = true;
848 if (!aRelative) {
849 if (!mOpenerScreenRect.IsEmpty()) {
850 screen = screenmgr->ScreenForRect(mOpenerScreenRect);
851 } else {
852 screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
856 if (aScreen && screen) {
857 rect = screen->GetAvailRectDisplayPix();
858 screenCoordinates = true;
861 if (!screenCoordinates && !windowCoordinates) {
862 return NS_ERROR_FAILURE;
865 NS_ASSERTION(mWindow, "what, no window?");
866 const LayoutDeviceIntSize ourDevSize = GetSize();
867 const DesktopIntSize ourSize =
868 RoundedToInt(ourDevSize / DevicePixelsPerDesktopPixel());
869 auto newPos =
870 rect.TopLeft() +
871 DesktopIntPoint((rect.width - ourSize.width) / 2,
872 (rect.height - ourSize.height) / (aAlert ? 3 : 2));
873 if (windowCoordinates) {
874 mWindow->ConstrainPosition(newPos);
877 SetPositionDesktopPix(newPos.x, newPos.y);
879 // If moving the window caused it to change size, re-do the centering.
880 if (GetSize() != ourDevSize) {
881 return Center(aRelative, aScreen, aAlert);
883 return NS_OK;
886 NS_IMETHODIMP AppWindow::Repaint(bool aForce) {
887 // XXX First Check In
888 NS_ASSERTION(false, "Not Yet Implemented");
889 return NS_OK;
892 NS_IMETHODIMP AppWindow::GetParentWidget(nsIWidget** aParentWidget) {
893 NS_ENSURE_ARG_POINTER(aParentWidget);
894 NS_ENSURE_STATE(mWindow);
896 NS_IF_ADDREF(*aParentWidget = mWindow->GetParent());
897 return NS_OK;
900 NS_IMETHODIMP AppWindow::SetParentWidget(nsIWidget* aParentWidget) {
901 // XXX First Check In
902 NS_ASSERTION(false, "Not Yet Implemented");
903 return NS_OK;
906 NS_IMETHODIMP AppWindow::GetParentNativeWindow(
907 nativeWindow* aParentNativeWindow) {
908 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
910 nsCOMPtr<nsIWidget> parentWidget;
911 NS_ENSURE_SUCCESS(GetParentWidget(getter_AddRefs(parentWidget)),
912 NS_ERROR_FAILURE);
914 if (parentWidget) {
915 *aParentNativeWindow = parentWidget->GetNativeData(NS_NATIVE_WIDGET);
918 return NS_OK;
921 NS_IMETHODIMP AppWindow::SetParentNativeWindow(
922 nativeWindow aParentNativeWindow) {
923 // XXX First Check In
924 NS_ASSERTION(false, "Not Yet Implemented");
925 return NS_OK;
928 NS_IMETHODIMP AppWindow::GetNativeHandle(nsAString& aNativeHandle) {
929 nsCOMPtr<nsIWidget> mainWidget;
930 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(mainWidget)),
931 NS_ERROR_FAILURE);
933 if (mainWidget) {
934 nativeWindow nativeWindowPtr = mainWidget->GetNativeData(NS_NATIVE_WINDOW);
935 /* the nativeWindow pointer is converted to and exposed as a string. This
936 is a more reliable way not to lose information (as opposed to JS
937 |Number| for instance) */
938 aNativeHandle =
939 NS_ConvertASCIItoUTF16(nsPrintfCString("0x%p", nativeWindowPtr));
942 return NS_OK;
945 NS_IMETHODIMP AppWindow::GetVisibility(bool* aVisibility) {
946 NS_ENSURE_ARG_POINTER(aVisibility);
948 // Always claim to be visible for now. See bug
949 // https://bugzilla.mozilla.org/show_bug.cgi?id=306245.
951 *aVisibility = true;
953 return NS_OK;
956 NS_IMETHODIMP AppWindow::SetVisibility(bool aVisibility) {
957 if (!mChromeLoaded) {
958 mShowAfterLoad = aVisibility;
959 return NS_OK;
962 if (mDebuting) {
963 return NS_OK;
966 NS_ENSURE_STATE(mDocShell);
968 mDebuting = true; // (Show / Focus is recursive)
970 // XXXTAB Do we really need to show docshell and the window? Isn't
971 // the window good enough?
972 mDocShell->SetVisibility(aVisibility);
973 // Store locally so it doesn't die on us. 'Show' can result in the window
974 // being closed with AppWindow::Destroy being called. That would set
975 // mWindow to null and posibly destroy the nsIWidget while its Show method
976 // is on the stack. We need to keep it alive until Show finishes.
977 nsCOMPtr<nsIWidget> window = mWindow;
978 window->Show(aVisibility);
980 nsCOMPtr<nsIWindowMediator> windowMediator(
981 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
982 if (windowMediator)
983 windowMediator->UpdateWindowTimeStamp(static_cast<nsIAppWindow*>(this));
985 // notify observers so that we can hide the splash screen if possible
986 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
987 NS_ASSERTION(obssvc, "Couldn't get observer service.");
988 if (obssvc) {
989 obssvc->NotifyObservers(static_cast<nsIAppWindow*>(this),
990 "xul-window-visible", nullptr);
993 mDebuting = false;
994 return NS_OK;
997 NS_IMETHODIMP AppWindow::GetEnabled(bool* aEnabled) {
998 NS_ENSURE_ARG_POINTER(aEnabled);
1000 if (mWindow) {
1001 *aEnabled = mWindow->IsEnabled();
1002 return NS_OK;
1005 *aEnabled = true; // better guess than most
1006 return NS_ERROR_FAILURE;
1009 NS_IMETHODIMP AppWindow::SetEnabled(bool aEnable) {
1010 if (mWindow) {
1011 mWindow->Enable(aEnable);
1012 return NS_OK;
1014 return NS_ERROR_FAILURE;
1017 NS_IMETHODIMP AppWindow::GetMainWidget(nsIWidget** aMainWidget) {
1018 NS_ENSURE_ARG_POINTER(aMainWidget);
1019 NS_IF_ADDREF(*aMainWidget = mWindow);
1020 return NS_OK;
1023 NS_IMETHODIMP AppWindow::GetTitle(nsAString& aTitle) {
1024 aTitle = mTitle;
1025 return NS_OK;
1028 NS_IMETHODIMP AppWindow::SetTitle(const nsAString& aTitle) {
1029 NS_ENSURE_STATE(mWindow);
1030 mTitle.Assign(aTitle);
1031 mTitle.StripCRLF();
1032 NS_ENSURE_SUCCESS(mWindow->SetTitle(mTitle), NS_ERROR_FAILURE);
1033 return NS_OK;
1036 //*****************************************************************************
1037 // AppWindow: Helpers
1038 //*****************************************************************************
1040 NS_IMETHODIMP AppWindow::EnsureChromeTreeOwner() {
1041 if (mChromeTreeOwner) return NS_OK;
1043 mChromeTreeOwner = new nsChromeTreeOwner();
1044 NS_ADDREF(mChromeTreeOwner);
1045 mChromeTreeOwner->AppWindow(this);
1047 return NS_OK;
1050 NS_IMETHODIMP AppWindow::EnsureContentTreeOwner() {
1051 if (mContentTreeOwner) return NS_OK;
1053 mContentTreeOwner = new nsContentTreeOwner(false);
1054 NS_ADDREF(mContentTreeOwner);
1055 mContentTreeOwner->AppWindow(this);
1057 return NS_OK;
1060 NS_IMETHODIMP AppWindow::EnsurePrimaryContentTreeOwner() {
1061 if (mPrimaryContentTreeOwner) return NS_OK;
1063 mPrimaryContentTreeOwner = new nsContentTreeOwner(true);
1064 NS_ADDREF(mPrimaryContentTreeOwner);
1065 mPrimaryContentTreeOwner->AppWindow(this);
1067 return NS_OK;
1070 NS_IMETHODIMP AppWindow::EnsurePrompter() {
1071 if (mPrompter) return NS_OK;
1073 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1074 nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow));
1075 if (NS_SUCCEEDED(rv)) {
1076 nsCOMPtr<nsIWindowWatcher> wwatch =
1077 do_GetService(NS_WINDOWWATCHER_CONTRACTID);
1078 if (wwatch) wwatch->GetNewPrompter(ourWindow, getter_AddRefs(mPrompter));
1080 return mPrompter ? NS_OK : NS_ERROR_FAILURE;
1083 NS_IMETHODIMP AppWindow::EnsureAuthPrompter() {
1084 if (mAuthPrompter) return NS_OK;
1086 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1087 nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow));
1088 if (NS_SUCCEEDED(rv)) {
1089 nsCOMPtr<nsIWindowWatcher> wwatch(
1090 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
1091 if (wwatch)
1092 wwatch->GetNewAuthPrompter(ourWindow, getter_AddRefs(mAuthPrompter));
1094 return mAuthPrompter ? NS_OK : NS_ERROR_FAILURE;
1097 NS_IMETHODIMP AppWindow::GetAvailScreenSize(int32_t* aAvailWidth,
1098 int32_t* aAvailHeight) {
1099 nsCOMPtr<mozIDOMWindowProxy> domWindow;
1100 GetWindowDOMWindow(getter_AddRefs(domWindow));
1101 NS_ENSURE_STATE(domWindow);
1103 auto* window = nsGlobalWindowOuter::Cast(domWindow);
1105 RefPtr<nsScreen> screen = window->GetScreen();
1106 NS_ENSURE_STATE(screen);
1108 *aAvailWidth = screen->AvailWidth();
1109 *aAvailHeight = screen->AvailHeight();
1110 return NS_OK;
1113 // Rounds window size to 1000x1000, or, if there isn't enough available
1114 // screen space, to a multiple of 200x100.
1115 NS_IMETHODIMP AppWindow::ForceRoundedDimensions() {
1116 if (mIsHiddenWindow) {
1117 return NS_OK;
1120 CSSToLayoutDeviceScale scale = UnscaledDevicePixelsPerCSSPixel();
1122 CSSIntSize availSizeCSS;
1123 GetAvailScreenSize(&availSizeCSS.width, &availSizeCSS.height);
1125 // To get correct chrome size, we have to resize the window to a proper
1126 // size first. So, here, we size it to its available size.
1127 SetSpecifiedSize(availSizeCSS.width, availSizeCSS.height);
1129 // Get the current window size for calculating chrome UI size.
1130 CSSIntSize windowSizeCSS = RoundedToInt(GetSize() / scale);
1132 // Get the content size for calculating chrome UI size.
1133 LayoutDeviceIntSize contentSizeDev;
1134 GetPrimaryContentSize(&contentSizeDev.width, &contentSizeDev.height);
1135 CSSIntSize contentSizeCSS = RoundedToInt(contentSizeDev / scale);
1137 // Calculate the chrome UI size.
1138 CSSIntSize chromeSizeCSS = windowSizeCSS - contentSizeCSS;
1140 CSSIntSize targetSizeCSS;
1141 // Here, we use the available screen dimensions as the input dimensions to
1142 // force the window to be rounded as the maximum available content size.
1143 nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
1144 chromeSizeCSS.width, chromeSizeCSS.height, availSizeCSS.width,
1145 availSizeCSS.height, availSizeCSS.width, availSizeCSS.height,
1146 false, // aSetOuterWidth
1147 false, // aSetOuterHeight
1148 &targetSizeCSS.width, &targetSizeCSS.height);
1150 LayoutDeviceIntSize targetSizeDev = RoundedToInt(targetSizeCSS * scale);
1152 SetPrimaryContentSize(targetSizeDev.width, targetSizeDev.height);
1154 return NS_OK;
1157 void AppWindow::OnChromeLoaded() {
1158 nsresult rv = EnsureContentTreeOwner();
1160 if (NS_SUCCEEDED(rv)) {
1161 mChromeLoaded = true;
1162 ApplyChromeFlags();
1163 SyncAttributesToWidget();
1164 if (mWindow) {
1165 SizeShell();
1166 if (mShowAfterLoad) {
1167 SetVisibility(true);
1169 AddTooltipSupport();
1171 // At this point the window may have been closed already during Show() or
1172 // SyncAttributesToWidget(), so AppWindow::Destroy may already have been
1173 // called. Take care!
1175 mPersistentAttributesMask += AllPersistentAttributes();
1178 bool AppWindow::NeedsTooltipListener() {
1179 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1180 if (!docShellElement || docShellElement->IsXULElement()) {
1181 // Tooltips in XUL are handled by each element.
1182 return false;
1184 // All other non-XUL document types need a tooltip listener.
1185 return true;
1188 void AppWindow::AddTooltipSupport() {
1189 if (!NeedsTooltipListener()) {
1190 return;
1192 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
1193 if (!listener) {
1194 return;
1197 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1198 MOZ_ASSERT(docShellElement);
1199 listener->AddTooltipSupport(docShellElement);
1202 void AppWindow::RemoveTooltipSupport() {
1203 if (!NeedsTooltipListener()) {
1204 return;
1206 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
1207 if (!listener) {
1208 return;
1211 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1212 MOZ_ASSERT(docShellElement);
1213 listener->RemoveTooltipSupport(docShellElement);
1216 static Maybe<int32_t> ReadIntAttribute(const Element& aElement,
1217 nsAtom* aPrimary,
1218 nsAtom* aSecondary = nullptr) {
1219 nsAutoString attrString;
1220 if (!aElement.GetAttr(aPrimary, attrString)) {
1221 if (aSecondary) {
1222 return ReadIntAttribute(aElement, aSecondary);
1224 return Nothing();
1227 nsresult res = NS_OK;
1228 int32_t ret = attrString.ToInteger(&res);
1229 return NS_SUCCEEDED(res) ? Some(ret) : Nothing();
1232 // If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes
1233 // to fit to the screen when staggering windows; if they're negative,
1234 // we use the window's current size instead.
1235 bool AppWindow::LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight) {
1236 bool gotPosition = false;
1238 // if we're the hidden window, don't try to validate our size/position. We're
1239 // special.
1240 if (mIsHiddenWindow) {
1241 return false;
1244 RefPtr<dom::Element> root = GetWindowDOMElement();
1245 NS_ENSURE_TRUE(root, false);
1247 const LayoutDeviceIntRect devRect = GetPositionAndSize();
1249 // Convert to global display pixels for consistent window management across
1250 // screens with diverse resolutions
1251 const DesktopIntPoint curPoint =
1252 RoundedToInt(devRect.TopLeft() / DevicePixelsPerDesktopPixel());
1254 // For size, use specified value if > 0, else current value
1255 CSSIntSize cssSize(aSpecWidth, aSpecHeight);
1257 CSSIntSize currentSize =
1258 RoundedToInt(devRect.Size() / UnscaledDevicePixelsPerCSSPixel());
1259 if (aSpecHeight <= 0) {
1260 cssSize.height = currentSize.height;
1262 if (aSpecWidth <= 0) {
1263 cssSize.width = currentSize.width;
1267 // Obtain the position information from the <xul:window> element.
1268 DesktopIntPoint specPoint = curPoint;
1270 // Also read lowercase screenx/y because the front-end sometimes sets these
1271 // via setAttribute on HTML documents like about:blank, and stuff gets
1272 // lowercased.
1274 // TODO(emilio): We should probably rename screenX/Y to screen-x/y to
1275 // prevent this impedance mismatch.
1276 if (auto attr =
1277 ReadIntAttribute(*root, nsGkAtoms::screenX, nsGkAtoms::screenx)) {
1278 specPoint.x = *attr;
1279 gotPosition = true;
1282 if (auto attr =
1283 ReadIntAttribute(*root, nsGkAtoms::screenY, nsGkAtoms::screeny)) {
1284 specPoint.y = *attr;
1285 gotPosition = true;
1288 if (gotPosition) {
1289 // Our position will be relative to our parent, if any
1290 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
1291 if (parent) {
1292 const DesktopIntPoint parentPos = RoundedToInt(
1293 parent->GetPosition() / parent->DevicePixelsPerDesktopPixel());
1294 specPoint += parentPos;
1295 } else {
1296 StaggerPosition(specPoint.x.value, specPoint.y.value, cssSize.width,
1297 cssSize.height);
1300 mWindow->ConstrainPosition(specPoint);
1301 if (specPoint != curPoint) {
1302 SetPositionDesktopPix(specPoint.x, specPoint.y);
1305 return gotPosition;
1308 static Maybe<int32_t> ReadSize(const Element& aElement, nsAtom* aAttr,
1309 nsAtom* aMinAttr, nsAtom* aMaxAttr) {
1310 Maybe<int32_t> attr = ReadIntAttribute(aElement, aAttr);
1311 if (!attr) {
1312 return Nothing();
1315 int32_t min =
1316 std::max(100, ReadIntAttribute(aElement, aMinAttr).valueOr(100));
1317 int32_t max = ReadIntAttribute(aElement, aMaxAttr)
1318 .valueOr(std::numeric_limits<int32_t>::max());
1320 return Some(std::min(max, std::max(*attr, min)));
1323 bool AppWindow::LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight) {
1324 bool gotSize = false;
1326 // if we're the hidden window, don't try to validate our size/position. We're
1327 // special.
1328 if (mIsHiddenWindow) {
1329 return false;
1332 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1333 NS_ENSURE_TRUE(windowElement, false);
1335 // Obtain the sizing information from the <xul:window> element.
1336 aSpecWidth = 100;
1337 aSpecHeight = 100;
1339 if (auto width = ReadSize(*windowElement, nsGkAtoms::width,
1340 nsGkAtoms::minwidth, nsGkAtoms::maxwidth)) {
1341 aSpecWidth = *width;
1342 gotSize = true;
1345 if (auto height = ReadSize(*windowElement, nsGkAtoms::height,
1346 nsGkAtoms::minheight, nsGkAtoms::maxheight)) {
1347 aSpecHeight = *height;
1348 gotSize = true;
1351 return gotSize;
1354 void AppWindow::SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight) {
1355 // These are in CSS pixels of the main window.
1356 // TODO(emilio): In my testing we usually have a pres context around, can we
1357 // just use it? That'd simplify the coordinate calculations.
1359 int32_t screenWidth;
1360 int32_t screenHeight;
1362 if (NS_SUCCEEDED(GetAvailScreenSize(&screenWidth, &screenHeight))) {
1363 if (aSpecWidth > screenWidth) {
1364 aSpecWidth = screenWidth;
1366 if (aSpecHeight > screenHeight) {
1367 aSpecHeight = screenHeight;
1372 NS_ASSERTION(mWindow, "we expected to have a window already");
1374 mIntrinsicallySized = false;
1376 // Convert specified values to device pixels, and resize
1377 auto newSize = RoundedToInt(CSSIntSize(aSpecWidth, aSpecHeight) *
1378 UnscaledDevicePixelsPerCSSPixel());
1380 // Note: Because of the asynchronous resizing on Linux we have to call
1381 // SetSize even when the size doesn't appear to change. A previous call that
1382 // has yet to complete can still change the size. We want the latest call to
1383 // define the final size.
1384 SetSize(newSize.width, newSize.height, false);
1387 /* Miscellaneous persistent attributes are attributes named in the
1388 |persist| attribute, other than size and position. Those are special
1389 because it's important to load those before one of the misc
1390 attributes (sizemode) and they require extra processing. */
1391 bool AppWindow::UpdateWindowStateFromMiscXULAttributes() {
1392 bool gotState = false;
1394 /* There are no misc attributes of interest to the hidden window.
1395 It's especially important not to try to validate that window's
1396 size or position, because some platforms (Mac OS X) need to
1397 make it visible and offscreen. */
1398 if (mIsHiddenWindow) return false;
1400 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1401 NS_ENSURE_TRUE(windowElement, false);
1403 nsAutoString stateString;
1404 nsSizeMode sizeMode = nsSizeMode_Normal;
1406 // If we are told to ignore the size mode attribute, force
1407 // normal sizemode.
1408 if (mIgnoreXULSizeMode) {
1409 windowElement->SetAttr(nsGkAtoms::sizemode, SIZEMODE_NORMAL,
1410 IgnoreErrors());
1411 } else {
1412 // Otherwise, read sizemode from DOM and, if the window is resizable,
1413 // set it later.
1414 windowElement->GetAttr(nsGkAtoms::sizemode, stateString);
1415 if ((stateString.Equals(SIZEMODE_MAXIMIZED) ||
1416 stateString.Equals(SIZEMODE_FULLSCREEN))) {
1417 /* Honor request to maximize only if the window is sizable.
1418 An unsizable, unmaximizable, yet maximized window confuses
1419 Windows OS and is something of a travesty, anyway. */
1420 if (mChromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
1421 mIntrinsicallySized = false;
1423 if (stateString.Equals(SIZEMODE_MAXIMIZED))
1424 sizeMode = nsSizeMode_Maximized;
1425 else
1426 sizeMode = nsSizeMode_Fullscreen;
1431 if (sizeMode == nsSizeMode_Fullscreen) {
1432 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1433 GetWindowDOMWindow(getter_AddRefs(ourWindow));
1434 auto* piWindow = nsPIDOMWindowOuter::From(ourWindow);
1435 piWindow->SetFullScreen(true);
1436 } else {
1437 // For maximized windows, ignore the XUL size and position attributes,
1438 // as setting them would set the window back to normal sizemode.
1439 if (sizeMode == nsSizeMode_Maximized) {
1440 mIgnoreXULSize = true;
1441 mIgnoreXULPosition = true;
1443 mWindow->SetSizeMode(sizeMode);
1445 gotState = true;
1447 // zlevel
1448 windowElement->GetAttr(nsGkAtoms::zlevel, stateString);
1449 if (!stateString.IsEmpty()) {
1450 nsresult errorCode;
1451 int32_t zLevel = stateString.ToInteger(&errorCode);
1452 if (NS_SUCCEEDED(errorCode) && zLevel >= lowestZ && zLevel <= highestZ)
1453 SetZLevel(zLevel);
1456 return gotState;
1459 /* Stagger windows of the same type so they don't appear on top of each other.
1460 This code does have a scary double loop -- it'll keep passing through
1461 the entire list of open windows until it finds a non-collision. Doesn't
1462 seem to be a problem, but it deserves watching.
1463 The aRequested{X,Y} parameters here are in desktop pixels;
1464 the aSpec{Width,Height} parameters are CSS pixel dimensions.
1466 void AppWindow::StaggerPosition(int32_t& aRequestedX, int32_t& aRequestedY,
1467 int32_t aSpecWidth, int32_t aSpecHeight) {
1468 // These "constants" will be converted from CSS to desktop pixels
1469 // for the appropriate screen, assuming we find a screen to use...
1470 // hence they're not actually declared const here.
1471 int32_t kOffset = 22;
1472 uint32_t kSlop = 4;
1474 bool keepTrying;
1475 int bouncedX = 0, // bounced off vertical edge of screen
1476 bouncedY = 0; // bounced off horizontal edge
1478 // look for any other windows of this type
1479 nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
1480 if (!wm) return;
1482 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1483 if (!windowElement) return;
1485 nsCOMPtr<nsIAppWindow> ourAppWindow(this);
1487 nsAutoString windowType;
1488 windowElement->GetAttr(nsGkAtoms::windowtype, windowType);
1490 DesktopIntRect screenRect;
1491 bool gotScreen = false;
1493 { // fetch screen coordinates
1494 nsCOMPtr<nsIScreenManager> screenMgr(
1495 do_GetService("@mozilla.org/gfx/screenmanager;1"));
1496 if (screenMgr) {
1497 nsCOMPtr<nsIScreen> ourScreen;
1498 // The coordinates here are already display pixels
1499 // XXX aSpecWidth and aSpecHeight are CSS pixels!
1500 screenMgr->ScreenForRect(aRequestedX, aRequestedY, aSpecWidth,
1501 aSpecHeight, getter_AddRefs(ourScreen));
1502 if (ourScreen) {
1503 screenRect = ourScreen->GetAvailRectDisplayPix();
1505 // Get the screen's scaling factors and convert staggering constants
1506 // from CSS px to desktop pixel units
1507 auto scale = ourScreen->GetCSSToDesktopScale();
1508 kOffset = (CSSCoord(kOffset) * scale).Rounded();
1509 kSlop = (CSSCoord(kSlop) * scale).Rounded();
1510 // Convert dimensions from CSS to desktop pixels
1511 aSpecWidth = (CSSCoord(aSpecWidth) * scale).Rounded();
1512 aSpecHeight = (CSSCoord(aSpecHeight) * scale).Rounded();
1513 gotScreen = true;
1518 // One full pass through all windows of this type, repeat until no collisions.
1519 do {
1520 keepTrying = false;
1521 nsCOMPtr<nsISimpleEnumerator> windowList;
1522 wm->GetAppWindowEnumerator(windowType.get(), getter_AddRefs(windowList));
1524 if (!windowList) break;
1526 // One full pass through all windows of this type, offset and stop on
1527 // collision.
1528 do {
1529 bool more;
1530 windowList->HasMoreElements(&more);
1531 if (!more) break;
1533 nsCOMPtr<nsISupports> supportsWindow;
1534 windowList->GetNext(getter_AddRefs(supportsWindow));
1536 nsCOMPtr<nsIAppWindow> listAppWindow(do_QueryInterface(supportsWindow));
1537 if (listAppWindow != ourAppWindow) {
1538 int32_t listX, listY;
1539 nsCOMPtr<nsIBaseWindow> listBaseWindow(
1540 do_QueryInterface(supportsWindow));
1541 listBaseWindow->GetPosition(&listX, &listY);
1542 double scale;
1543 if (NS_SUCCEEDED(
1544 listBaseWindow->GetDevicePixelsPerDesktopPixel(&scale))) {
1545 listX = NSToIntRound(listX / scale);
1546 listY = NSToIntRound(listY / scale);
1549 if (Abs(listX - aRequestedX) <= kSlop &&
1550 Abs(listY - aRequestedY) <= kSlop) {
1551 // collision! offset and start over
1552 if (bouncedX & 0x1)
1553 aRequestedX -= kOffset;
1554 else
1555 aRequestedX += kOffset;
1556 aRequestedY += kOffset;
1558 if (gotScreen) {
1559 // if we're moving to the right and we need to bounce...
1560 if (!(bouncedX & 0x1) &&
1561 ((aRequestedX + aSpecWidth) > screenRect.XMost())) {
1562 aRequestedX = screenRect.XMost() - aSpecWidth;
1563 ++bouncedX;
1566 // if we're moving to the left and we need to bounce...
1567 if ((bouncedX & 0x1) && aRequestedX < screenRect.X()) {
1568 aRequestedX = screenRect.X();
1569 ++bouncedX;
1572 // if we hit the bottom then bounce to the top
1573 if (aRequestedY + aSpecHeight > screenRect.YMost()) {
1574 aRequestedY = screenRect.Y();
1575 ++bouncedY;
1579 /* loop around again,
1580 but it's time to give up once we've covered the screen.
1581 there's a potential infinite loop with lots of windows. */
1582 keepTrying = bouncedX < 2 || bouncedY == 0;
1583 break;
1586 } while (true);
1587 } while (keepTrying);
1590 void AppWindow::SyncAttributesToWidget() {
1591 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1592 if (!windowElement) return;
1594 MOZ_DIAGNOSTIC_ASSERT(mWindow, "No widget on SyncAttributesToWidget?");
1596 nsAutoString attr;
1598 // Some attributes can change the client size (e.g. chromemargin on Windows
1599 // and MacOS). But we might want to keep it.
1600 const LayoutDeviceIntSize oldClientSize = mWindow->GetClientSize();
1601 // We have to check now whether we want to restore the client size, as any
1602 // change in size will reset its state.
1603 bool maintainClientSize = mDominantClientSize;
1605 // "hidechrome" attribute
1606 if (windowElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidechrome,
1607 nsGkAtoms::_true, eCaseMatters)) {
1608 mWindow->HideWindowChrome(true);
1611 NS_ENSURE_TRUE_VOID(mWindow);
1613 // "chromemargin" attribute
1614 nsIntMargin margins;
1615 windowElement->GetAttribute(u"chromemargin"_ns, attr);
1616 if (nsContentUtils::ParseIntMarginValue(attr, margins)) {
1617 mWindow->SetNonClientMargins(
1618 LayoutDeviceIntMargin::FromUnknownMargin(margins));
1621 NS_ENSURE_TRUE_VOID(mWindow);
1623 // "windowtype", "windowclass", "windowname" attributes
1624 nsAutoString windowClassAttr, windowNameAttr;
1625 windowElement->GetAttr(nsGkAtoms::windowtype, attr);
1626 windowElement->GetAttribute(u"windowclass"_ns, windowClassAttr);
1627 windowElement->GetAttribute(u"windowname"_ns, windowNameAttr);
1628 mWindow->SetWindowClass(attr, windowClassAttr, windowNameAttr);
1630 NS_ENSURE_TRUE_VOID(mWindow);
1632 // "icon" attribute
1633 windowElement->GetAttribute(u"icon"_ns, attr);
1634 if (!attr.IsEmpty()) {
1635 mWindow->SetIcon(attr);
1637 NS_ENSURE_TRUE_VOID(mWindow);
1640 // "drawtitle" attribute
1641 windowElement->GetAttribute(u"drawtitle"_ns, attr);
1642 mWindow->SetDrawsTitle(attr.LowerCaseEqualsLiteral("true"));
1644 NS_ENSURE_TRUE_VOID(mWindow);
1646 // "toggletoolbar" attribute
1647 windowElement->GetAttribute(u"toggletoolbar"_ns, attr);
1648 mWindow->SetShowsToolbarButton(attr.LowerCaseEqualsLiteral("true"));
1650 NS_ENSURE_TRUE_VOID(mWindow);
1652 // "macnativefullscreen" attribute
1653 windowElement->GetAttribute(u"macnativefullscreen"_ns, attr);
1654 mWindow->SetSupportsNativeFullscreen(attr.LowerCaseEqualsLiteral("true"));
1656 NS_ENSURE_TRUE_VOID(mWindow);
1658 // "macanimationtype" attribute
1659 windowElement->GetAttribute(u"macanimationtype"_ns, attr);
1660 if (attr.EqualsLiteral("document")) {
1661 mWindow->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation);
1664 // Check if the client size did change and if we want to restore it.
1665 if (maintainClientSize && mWindow->SizeMode() == nsSizeMode_Normal &&
1666 oldClientSize != mWindow->GetClientSize()) {
1667 mWindow->ResizeClient(oldClientSize / mWindow->GetDesktopToDeviceScale(),
1668 true);
1669 mDominantClientSize = true;
1673 enum class ConversionDirection {
1674 InnerToOuter,
1675 OuterToInner,
1678 static void ConvertWindowSize(nsIAppWindow* aWin, const nsAtom* aAttr,
1679 ConversionDirection aDirection,
1680 nsAString& aInOutString) {
1681 MOZ_ASSERT(aWin);
1682 MOZ_ASSERT(aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height);
1684 nsresult rv;
1685 int32_t size = aInOutString.ToInteger(&rv);
1686 if (NS_FAILED(rv)) {
1687 return;
1690 int32_t sizeDiff = aAttr == nsGkAtoms::width
1691 ? aWin->GetOuterToInnerWidthDifferenceInCSSPixels()
1692 : aWin->GetOuterToInnerHeightDifferenceInCSSPixels();
1694 if (!sizeDiff) {
1695 return;
1698 int32_t multiplier = aDirection == ConversionDirection::InnerToOuter ? 1 : -1;
1700 CopyASCIItoUTF16(nsPrintfCString("%d", size + multiplier * sizeDiff),
1701 aInOutString);
1704 nsresult AppWindow::GetPersistentValue(const nsAtom* aAttr, nsAString& aValue) {
1705 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1706 if (!docShellElement) {
1707 return NS_ERROR_FAILURE;
1710 nsAutoString windowElementId;
1711 docShellElement->GetId(windowElementId);
1712 // Elements must have an ID to be persisted.
1713 if (windowElementId.IsEmpty()) {
1714 return NS_OK;
1717 RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
1718 nsIURI* docURI = ownerDoc->GetDocumentURI();
1719 if (!docURI) {
1720 return NS_ERROR_FAILURE;
1722 nsAutoCString utf8uri;
1723 nsresult rv = docURI->GetSpec(utf8uri);
1724 NS_ENSURE_SUCCESS(rv, rv);
1725 NS_ConvertUTF8toUTF16 uri(utf8uri);
1727 if (!mLocalStore) {
1728 mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1729 if (NS_WARN_IF(!mLocalStore)) {
1730 return NS_ERROR_NOT_INITIALIZED;
1734 rv = mLocalStore->GetValue(uri, windowElementId, nsDependentAtomString(aAttr),
1735 aValue);
1736 if (NS_WARN_IF(NS_FAILED(rv))) {
1737 return rv;
1740 if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
1741 // Convert attributes from outer size to inner size for top-level
1742 // windows, see bug 1444525 & co.
1743 ConvertWindowSize(this, aAttr, ConversionDirection::OuterToInner, aValue);
1746 return NS_OK;
1749 nsresult AppWindow::GetDocXulStoreKeys(nsString& aUriSpec,
1750 nsString& aWindowElementId) {
1751 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1752 if (!docShellElement) {
1753 return NS_ERROR_FAILURE;
1756 docShellElement->GetId(aWindowElementId);
1757 // Match the behavior of XULPersist and only persist values if the element
1758 // has an ID.
1759 if (aWindowElementId.IsEmpty()) {
1760 return NS_OK;
1763 RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
1764 nsIURI* docURI = ownerDoc->GetDocumentURI();
1765 if (!docURI) {
1766 return NS_ERROR_FAILURE;
1769 nsAutoCString utf8uri;
1770 nsresult rv = docURI->GetSpec(utf8uri);
1771 if (NS_WARN_IF(NS_FAILED(rv))) {
1772 return rv;
1775 aUriSpec = NS_ConvertUTF8toUTF16(utf8uri);
1777 return NS_OK;
1780 nsresult AppWindow::MaybeSaveEarlyWindowPersistentValues(
1781 const LayoutDeviceIntRect& aRect) {
1782 #ifdef XP_WIN
1783 nsAutoString uri;
1784 nsAutoString windowElementId;
1785 nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
1787 if (NS_WARN_IF(NS_FAILED(rv))) {
1788 return rv;
1791 if (!windowElementId.EqualsLiteral("main-window") ||
1792 !uri.EqualsLiteral("chrome://browser/content/browser.xhtml")) {
1793 return NS_OK;
1796 SkeletonUISettings settings;
1798 settings.screenX = aRect.X();
1799 settings.screenY = aRect.Y();
1800 settings.width = aRect.Width();
1801 settings.height = aRect.Height();
1803 settings.maximized = mWindow->SizeMode() == nsSizeMode_Maximized;
1804 settings.cssToDevPixelScaling = UnscaledDevicePixelsPerCSSPixel().scale;
1806 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1807 Document* doc = windowElement->GetComposedDoc();
1808 Element* urlbarEl = doc->GetElementById(u"urlbar"_ns);
1810 nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow();
1811 nsCOMPtr<nsIDOMWindowUtils> utils =
1812 nsGlobalWindowOuter::Cast(window)->WindowUtils();
1813 RefPtr<dom::DOMRect> urlbarRect;
1814 rv = utils->GetBoundsWithoutFlushing(urlbarEl, getter_AddRefs(urlbarRect));
1815 if (NS_WARN_IF(NS_FAILED(rv))) {
1816 return rv;
1819 double urlbarX = urlbarRect->X();
1820 double urlbarWidth = urlbarRect->Width();
1822 // Hard-coding the following values and this behavior in general is rather
1823 // fragile, and can easily get out of sync with the actual front-end values.
1824 // This is not intended as a long-term solution, but only as the relatively
1825 // straightforward implementation of an experimental feature. If we want to
1826 // ship the skeleton UI to all users, we should strongly consider a more
1827 // robust solution than this. The vertical position of the urlbar will be
1828 // fixed.
1829 nsAutoString attributeValue;
1830 urlbarEl->GetAttribute(u"breakout-extend"_ns, attributeValue);
1831 // Scale down the urlbar if it is focused
1832 if (attributeValue.EqualsLiteral("true")) {
1833 // defined in browser.inc.css as 2px
1834 int urlbarBreakoutExtend = 2;
1835 // defined in urlbar-searchbar.inc.css as 5px
1836 int urlbarMarginInline = 5;
1838 // breakout-extend measurements are defined in urlbar-searchbar.inc.css
1839 urlbarX += (double)(urlbarBreakoutExtend + urlbarMarginInline);
1840 urlbarWidth -= (double)(2 * (urlbarBreakoutExtend + urlbarMarginInline));
1842 CSSPixelSpan urlbar;
1843 urlbar.start = urlbarX;
1844 urlbar.end = urlbar.start + urlbarWidth;
1845 settings.urlbarSpan = urlbar;
1847 Element* navbar = doc->GetElementById(u"nav-bar"_ns);
1849 Element* searchbarEl = doc->GetElementById(u"searchbar"_ns);
1850 CSSPixelSpan searchbar;
1851 if (navbar->Contains(searchbarEl)) {
1852 RefPtr<dom::DOMRect> searchbarRect;
1853 rv = utils->GetBoundsWithoutFlushing(searchbarEl,
1854 getter_AddRefs(searchbarRect));
1855 if (NS_WARN_IF(NS_FAILED(rv))) {
1856 return rv;
1858 searchbar.start = searchbarRect->X();
1859 searchbar.end = searchbar.start + searchbarRect->Width();
1860 } else {
1861 // There is no searchbar in the UI
1862 searchbar.start = 0;
1863 searchbar.end = 0;
1865 settings.searchbarSpan = searchbar;
1867 nsAutoString bookmarksVisibility;
1868 Preferences::GetString("browser.toolbars.bookmarks.visibility",
1869 bookmarksVisibility);
1870 settings.bookmarksToolbarShown =
1871 bookmarksVisibility.EqualsLiteral("always") ||
1872 bookmarksVisibility.EqualsLiteral("newtab");
1874 Element* menubar = doc->GetElementById(u"toolbar-menubar"_ns);
1875 menubar->GetAttribute(u"autohide"_ns, attributeValue);
1876 settings.menubarShown = attributeValue.EqualsLiteral("false");
1878 ErrorResult err;
1879 nsCOMPtr<nsIHTMLCollection> toolbarSprings = navbar->GetElementsByTagNameNS(
1880 u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns,
1881 u"toolbarspring"_ns, err);
1882 if (err.Failed()) {
1883 return NS_ERROR_FAILURE;
1885 mozilla::Vector<CSSPixelSpan> springs;
1886 for (size_t i = 0; i < toolbarSprings->Length(); i++) {
1887 RefPtr<Element> springEl = toolbarSprings->Item(i);
1888 RefPtr<dom::DOMRect> springRect;
1889 rv = utils->GetBoundsWithoutFlushing(springEl, getter_AddRefs(springRect));
1890 if (NS_WARN_IF(NS_FAILED(rv))) {
1891 return rv;
1893 CSSPixelSpan spring;
1894 spring.start = springRect->X();
1895 spring.end = spring.start + springRect->Width();
1896 if (!settings.springs.append(spring)) {
1897 return NS_ERROR_FAILURE;
1901 settings.rtlEnabled = intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1903 bool isInTabletMode = false;
1904 bool autoTouchModePref =
1905 Preferences::GetBool("browser.touchmode.auto", false);
1906 if (autoTouchModePref) {
1907 nsCOMPtr<nsIWindowsUIUtils> uiUtils(
1908 do_GetService("@mozilla.org/windows-ui-utils;1"));
1909 if (!NS_WARN_IF(!uiUtils)) {
1910 uiUtils->GetInTabletMode(&isInTabletMode);
1914 if (isInTabletMode) {
1915 settings.uiDensity = SkeletonUIDensity::Touch;
1916 } else {
1917 int uiDensityPref = Preferences::GetInt("browser.uidensity", 0);
1918 switch (uiDensityPref) {
1919 case 0: {
1920 settings.uiDensity = SkeletonUIDensity::Default;
1921 break;
1923 case 1: {
1924 settings.uiDensity = SkeletonUIDensity::Compact;
1925 break;
1927 case 2: {
1928 settings.uiDensity = SkeletonUIDensity::Touch;
1929 break;
1934 Unused << PersistPreXULSkeletonUIValues(settings);
1935 #endif
1937 return NS_OK;
1940 nsresult AppWindow::SetPersistentValue(const nsAtom* aAttr,
1941 const nsAString& aValue) {
1942 nsAutoString uri;
1943 nsAutoString windowElementId;
1944 nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
1946 if (NS_FAILED(rv) || windowElementId.IsEmpty()) {
1947 return rv;
1950 nsAutoString maybeConvertedValue(aValue);
1951 if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
1952 // Make sure we store the <window> attributes as outer window size, see
1953 // bug 1444525 & co.
1954 ConvertWindowSize(this, aAttr, ConversionDirection::InnerToOuter,
1955 maybeConvertedValue);
1958 if (!mLocalStore) {
1959 mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1960 if (NS_WARN_IF(!mLocalStore)) {
1961 return NS_ERROR_NOT_INITIALIZED;
1965 return mLocalStore->SetValue(
1966 uri, windowElementId, nsDependentAtomString(aAttr), maybeConvertedValue);
1969 void AppWindow::MaybeSavePersistentPositionAndSize(
1970 PersistentAttributes aAttributes, Element& aRootElement,
1971 const nsAString& aPersistString, bool aShouldPersist) {
1972 if ((aAttributes & PersistentAttributes{PersistentAttribute::Position,
1973 PersistentAttribute::Size})
1974 .isEmpty()) {
1975 return;
1978 // get our size, position and mode to persist
1979 LayoutDeviceIntRect rect;
1980 if (NS_FAILED(mWindow->GetRestoredBounds(rect))) {
1981 return;
1984 // we use CSS pixels for size, but desktop pixels for position
1985 CSSToLayoutDeviceScale sizeScale = UnscaledDevicePixelsPerCSSPixel();
1986 DesktopToLayoutDeviceScale posScale = DevicePixelsPerDesktopPixel();
1988 // make our position relative to our parent, if any
1989 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
1990 if (parent) {
1991 int32_t parentX, parentY;
1992 if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
1993 rect.MoveBy(-parentX, -parentY);
1997 nsAutoString sizeString;
1998 // (only for size elements which are persisted)
1999 if (aAttributes.contains(PersistentAttribute::Position)) {
2000 if (aPersistString.Find(u"screenX") >= 0) {
2001 sizeString.Truncate();
2002 sizeString.AppendInt(NSToIntRound(rect.X() / posScale.scale));
2003 aRootElement.SetAttr(nsGkAtoms::screenX, sizeString, IgnoreErrors());
2004 if (aShouldPersist) {
2005 Unused << SetPersistentValue(nsGkAtoms::screenX, sizeString);
2008 if (aPersistString.Find(u"screenY") >= 0) {
2009 sizeString.Truncate();
2010 sizeString.AppendInt(NSToIntRound(rect.Y() / posScale.scale));
2011 aRootElement.SetAttr(nsGkAtoms::screenY, sizeString, IgnoreErrors());
2012 if (aShouldPersist) {
2013 Unused << SetPersistentValue(nsGkAtoms::screenY, sizeString);
2018 if (aAttributes.contains(PersistentAttribute::Size)) {
2019 LayoutDeviceIntRect innerRect =
2020 rect - GetOuterToInnerSizeDifference(mWindow);
2021 if (aPersistString.Find(u"width") >= 0) {
2022 sizeString.Truncate();
2023 sizeString.AppendInt(NSToIntRound(innerRect.Width() / sizeScale.scale));
2024 aRootElement.SetAttr(nsGkAtoms::width, sizeString, IgnoreErrors());
2025 if (aShouldPersist) {
2026 Unused << SetPersistentValue(nsGkAtoms::width, sizeString);
2029 if (aPersistString.Find(u"height") >= 0) {
2030 sizeString.Truncate();
2031 sizeString.AppendInt(NSToIntRound(innerRect.Height() / sizeScale.scale));
2032 aRootElement.SetAttr(nsGkAtoms::height, sizeString, IgnoreErrors());
2033 if (aShouldPersist) {
2034 Unused << SetPersistentValue(nsGkAtoms::height, sizeString);
2039 Unused << MaybeSaveEarlyWindowPersistentValues(rect);
2042 void AppWindow::MaybeSavePersistentMiscAttributes(
2043 PersistentAttributes aAttributes, Element& aRootElement,
2044 const nsAString& aPersistString, bool aShouldPersist) {
2045 if (!aAttributes.contains(PersistentAttribute::Misc)) {
2046 return;
2049 nsSizeMode sizeMode = mWindow->SizeMode();
2050 nsAutoString sizeString;
2051 if (sizeMode != nsSizeMode_Minimized) {
2052 if (sizeMode == nsSizeMode_Maximized) {
2053 sizeString.Assign(SIZEMODE_MAXIMIZED);
2054 } else if (sizeMode == nsSizeMode_Fullscreen) {
2055 sizeString.Assign(SIZEMODE_FULLSCREEN);
2056 } else {
2057 sizeString.Assign(SIZEMODE_NORMAL);
2059 aRootElement.SetAttr(nsGkAtoms::sizemode, sizeString, IgnoreErrors());
2060 if (aShouldPersist && aPersistString.Find(u"sizemode") >= 0) {
2061 Unused << SetPersistentValue(nsGkAtoms::sizemode, sizeString);
2064 aRootElement.SetAttribute(u"gtktiledwindow"_ns,
2065 mWindow->IsTiled() ? u"true"_ns : u"false"_ns,
2066 IgnoreErrors());
2067 if (aPersistString.Find(u"zlevel") >= 0) {
2068 uint32_t zLevel;
2069 nsCOMPtr<nsIWindowMediator> mediator(
2070 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
2071 if (mediator) {
2072 mediator->GetZLevel(this, &zLevel);
2073 sizeString.Truncate();
2074 sizeString.AppendInt(zLevel);
2075 aRootElement.SetAttr(nsGkAtoms::zlevel, sizeString, IgnoreErrors());
2076 if (aShouldPersist) {
2077 Unused << SetPersistentValue(nsGkAtoms::zlevel, sizeString);
2083 void AppWindow::SavePersistentAttributes(
2084 const PersistentAttributes aAttributes) {
2085 // can happen when the persistence timer fires at an inopportune time
2086 // during window shutdown
2087 if (!mDocShell) {
2088 return;
2091 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
2092 if (!docShellElement) {
2093 return;
2096 nsAutoString persistString;
2097 docShellElement->GetAttr(nsGkAtoms::persist, persistString);
2098 if (persistString.IsEmpty()) { // quick check which sometimes helps
2099 mPersistentAttributesDirty.clear();
2100 return;
2103 bool shouldPersist = mWindow->SizeMode() != nsSizeMode_Fullscreen;
2104 MaybeSavePersistentPositionAndSize(aAttributes, *docShellElement,
2105 persistString, shouldPersist);
2106 MaybeSavePersistentMiscAttributes(aAttributes, *docShellElement,
2107 persistString, shouldPersist);
2108 mPersistentAttributesDirty -= aAttributes;
2111 NS_IMETHODIMP AppWindow::GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow) {
2112 NS_ENSURE_STATE(mDocShell);
2114 if (!mDOMWindow) mDOMWindow = mDocShell->GetWindow();
2115 NS_ENSURE_TRUE(mDOMWindow, NS_ERROR_FAILURE);
2117 *aDOMWindow = mDOMWindow;
2118 NS_ADDREF(*aDOMWindow);
2119 return NS_OK;
2122 dom::Element* AppWindow::GetWindowDOMElement() const {
2123 NS_ENSURE_TRUE(mDocShell, nullptr);
2125 nsCOMPtr<nsIDocumentViewer> viewer;
2126 mDocShell->GetDocViewer(getter_AddRefs(viewer));
2127 NS_ENSURE_TRUE(viewer, nullptr);
2129 const dom::Document* document = viewer->GetDocument();
2130 NS_ENSURE_TRUE(document, nullptr);
2132 return document->GetRootElement();
2135 nsresult AppWindow::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
2136 bool aPrimary) {
2137 // Set the default content tree owner
2138 if (aPrimary) {
2139 NS_ENSURE_SUCCESS(EnsurePrimaryContentTreeOwner(), NS_ERROR_FAILURE);
2140 aContentShell->SetTreeOwner(mPrimaryContentTreeOwner);
2141 mPrimaryContentShell = aContentShell;
2142 mPrimaryBrowserParent = nullptr;
2143 } else {
2144 NS_ENSURE_SUCCESS(EnsureContentTreeOwner(), NS_ERROR_FAILURE);
2145 aContentShell->SetTreeOwner(mContentTreeOwner);
2146 if (mPrimaryContentShell == aContentShell) mPrimaryContentShell = nullptr;
2149 return NS_OK;
2152 nsresult AppWindow::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
2153 if (mPrimaryContentShell == aContentShell) {
2154 mPrimaryContentShell = nullptr;
2156 return NS_OK;
2159 NS_IMETHODIMP
2160 AppWindow::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
2161 if (mPrimaryBrowserParent) {
2162 return GetPrimaryRemoteTabSize(aWidth, aHeight);
2164 if (mPrimaryContentShell) {
2165 return GetPrimaryContentShellSize(aWidth, aHeight);
2167 return NS_ERROR_UNEXPECTED;
2170 nsresult AppWindow::GetPrimaryRemoteTabSize(int32_t* aWidth, int32_t* aHeight) {
2171 BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
2172 // Need strong ref, since Client* can run script.
2173 RefPtr<dom::Element> element = host->GetOwnerElement();
2174 NS_ENSURE_STATE(element);
2176 CSSIntSize size(element->ClientWidth(), element->ClientHeight());
2177 LayoutDeviceIntSize sizeDev =
2178 RoundedToInt(size * UnscaledDevicePixelsPerCSSPixel());
2179 if (aWidth) {
2180 *aWidth = sizeDev.width;
2182 if (aHeight) {
2183 *aHeight = sizeDev.height;
2185 return NS_OK;
2188 nsresult AppWindow::GetPrimaryContentShellSize(int32_t* aWidth,
2189 int32_t* aHeight) {
2190 NS_ENSURE_STATE(mPrimaryContentShell);
2192 nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(mPrimaryContentShell));
2193 NS_ENSURE_STATE(shellWindow);
2195 LayoutDeviceIntSize sizeDev = shellWindow->GetSize();
2196 if (aWidth) {
2197 *aWidth = sizeDev.width;
2199 if (aHeight) {
2200 *aHeight = sizeDev.height;
2202 return NS_OK;
2205 NS_IMETHODIMP
2206 AppWindow::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
2207 if (mPrimaryBrowserParent) {
2208 return SetPrimaryRemoteTabSize(aWidth, aHeight);
2210 if (mPrimaryContentShell) {
2211 return SizeShellTo(mPrimaryContentShell, aWidth, aHeight);
2213 return NS_ERROR_UNEXPECTED;
2216 nsresult AppWindow::SetPrimaryRemoteTabSize(int32_t aWidth, int32_t aHeight) {
2217 int32_t shellWidth, shellHeight;
2218 GetPrimaryRemoteTabSize(&shellWidth, &shellHeight);
2219 SizeShellToWithLimit(aWidth, aHeight, shellWidth, shellHeight);
2220 return NS_OK;
2223 nsresult AppWindow::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
2224 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
2225 return mDocShell->GetSize(aWidth, aHeight);
2228 nsresult AppWindow::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
2229 return SizeShellTo(mDocShell, aWidth, aHeight);
2232 NS_IMETHODIMP AppWindow::SizeShellTo(nsIDocShellTreeItem* aShellItem,
2233 int32_t aCX, int32_t aCY) {
2234 MOZ_ASSERT(aShellItem == mDocShell || aShellItem == mPrimaryContentShell);
2235 if (aShellItem == mDocShell) {
2236 auto newSize =
2237 LayoutDeviceIntSize(aCX, aCY) + GetOuterToInnerSizeDifference(mWindow);
2238 SetSize(newSize.width, newSize.height, /* aRepaint = */ true);
2239 mDominantClientSize = true;
2240 return NS_OK;
2243 // XXXTAB This is wrong, we should actually reflow based on the passed in
2244 // shell. For now we are hacking and doing delta sizing. This is bad
2245 // because it assumes all size we add will go to the shell which probably
2246 // won't happen.
2247 nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
2248 NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
2250 int32_t width = 0;
2251 int32_t height = 0;
2252 shellAsWin->GetSize(&width, &height);
2254 SizeShellToWithLimit(aCX, aCY, width, height);
2256 return NS_OK;
2259 NS_IMETHODIMP AppWindow::ExitModalLoop(nsresult aStatus) {
2260 if (mContinueModalLoop) EnableParent(true);
2261 mContinueModalLoop = false;
2262 mModalStatus = aStatus;
2263 return NS_OK;
2266 // top-level function to create a new window
2267 NS_IMETHODIMP AppWindow::CreateNewWindow(int32_t aChromeFlags,
2268 nsIOpenWindowInfo* aOpenWindowInfo,
2269 nsIAppWindow** _retval) {
2270 NS_ENSURE_ARG_POINTER(_retval);
2272 if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME) {
2273 MOZ_RELEASE_ASSERT(
2274 !aOpenWindowInfo,
2275 "Unexpected nsOpenWindowInfo when creating a new chrome window");
2276 return CreateNewChromeWindow(aChromeFlags, _retval);
2279 return CreateNewContentWindow(aChromeFlags, aOpenWindowInfo, _retval);
2282 NS_IMETHODIMP AppWindow::CreateNewChromeWindow(int32_t aChromeFlags,
2283 nsIAppWindow** _retval) {
2284 nsCOMPtr<nsIAppShellService> appShell(
2285 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
2286 NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
2288 // Just do a normal create of a window and return.
2289 nsCOMPtr<nsIAppWindow> newWindow;
2290 appShell->CreateTopLevelWindow(
2291 this, nullptr, aChromeFlags, nsIAppShellService::SIZE_TO_CONTENT,
2292 nsIAppShellService::SIZE_TO_CONTENT, getter_AddRefs(newWindow));
2294 NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
2296 newWindow.forget(_retval);
2298 return NS_OK;
2301 NS_IMETHODIMP AppWindow::CreateNewContentWindow(
2302 int32_t aChromeFlags, nsIOpenWindowInfo* aOpenWindowInfo,
2303 nsIAppWindow** _retval) {
2304 nsCOMPtr<nsIAppShellService> appShell(
2305 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
2306 NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
2308 // We need to create a new top level window and then enter a nested
2309 // loop. Eventually the new window will be told that it has loaded,
2310 // at which time we know it is safe to spin out of the nested loop
2311 // and allow the opening code to proceed.
2313 nsCOMPtr<nsIURI> uri;
2314 nsAutoCString urlStr;
2315 urlStr.AssignLiteral(BROWSER_CHROME_URL_QUOTED);
2317 nsCOMPtr<nsIIOService> service(do_GetService(NS_IOSERVICE_CONTRACTID));
2318 if (service) {
2319 service->NewURI(urlStr, nullptr, nullptr, getter_AddRefs(uri));
2321 NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
2323 // We need to create a chrome window to contain the content window we're about
2324 // to pass back. The subject principal needs to be system while we're creating
2325 // it to make things work right, so force a system caller. See bug 799348
2326 // comment 13 for a description of what happens when we don't.
2327 nsCOMPtr<nsIAppWindow> newWindow;
2329 AutoNoJSAPI nojsapi;
2330 appShell->CreateTopLevelWindow(this, uri, aChromeFlags, 615, 480,
2331 getter_AddRefs(newWindow));
2332 NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
2335 AppWindow* appWin =
2336 static_cast<AppWindow*>(static_cast<nsIAppWindow*>(newWindow));
2338 // Specify which flags should be used by browser.xhtml to create the initial
2339 // content browser window.
2340 appWin->mInitialOpenWindowInfo = aOpenWindowInfo;
2342 // Specify that we want the window to remain locked until the chrome has
2343 // loaded.
2344 appWin->LockUntilChromeLoad();
2347 AutoNoJSAPI nojsapi;
2348 SpinEventLoopUntil("AppWindow::CreateNewContentWindow"_ns,
2349 [&]() { return !appWin->IsLocked(); });
2352 NS_ENSURE_STATE(appWin->mPrimaryContentShell ||
2353 appWin->mPrimaryBrowserParent);
2354 MOZ_ASSERT_IF(appWin->mPrimaryContentShell,
2355 !aOpenWindowInfo->GetNextRemoteBrowser());
2357 newWindow.forget(_retval);
2359 return NS_OK;
2362 NS_IMETHODIMP AppWindow::GetHasPrimaryContent(bool* aResult) {
2363 *aResult = mPrimaryBrowserParent || mPrimaryContentShell;
2364 return NS_OK;
2367 void AppWindow::EnableParent(bool aEnable) {
2368 nsCOMPtr<nsIBaseWindow> parentWindow;
2369 nsCOMPtr<nsIWidget> parentWidget;
2371 parentWindow = do_QueryReferent(mParentWindow);
2372 if (parentWindow) parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
2373 if (parentWidget) parentWidget->Enable(aEnable);
2376 // Constrain the window to its proper z-level
2377 bool AppWindow::ConstrainToZLevel(bool aImmediate, nsWindowZ* aPlacement,
2378 nsIWidget* aReqBelow,
2379 nsIWidget** aActualBelow) {
2380 #if 0
2381 /* Do we have a parent window? This means our z-order is already constrained,
2382 since we're a dependent window. Our window list isn't hierarchical,
2383 so we can't properly calculate placement for such a window.
2384 Should we just abort? */
2385 nsCOMPtr<nsIBaseWindow> parentWindow = do_QueryReferent(mParentWindow);
2386 if (parentWindow)
2387 return false;
2388 #endif
2390 nsCOMPtr<nsIWindowMediator> mediator(
2391 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
2392 if (!mediator) return false;
2394 bool altered;
2395 uint32_t position, newPosition, zLevel;
2396 nsIAppWindow* us = this;
2398 altered = false;
2399 mediator->GetZLevel(this, &zLevel);
2401 // translate from WidgetGUIEvent to nsIWindowMediator constants
2402 position = nsIWindowMediator::zLevelTop;
2403 if (*aPlacement == nsWindowZBottom || zLevel == nsIAppWindow::lowestZ)
2404 position = nsIWindowMediator::zLevelBottom;
2405 else if (*aPlacement == nsWindowZRelative)
2406 position = nsIWindowMediator::zLevelBelow;
2408 if (NS_SUCCEEDED(mediator->CalculateZPosition(
2409 us, position, aReqBelow, &newPosition, aActualBelow, &altered))) {
2410 /* If we were asked to move to the top but constrained to remain
2411 below one of our other windows, first move all windows in that
2412 window's layer and above to the top. This allows the user to
2413 click a window which can't be topmost and still bring mozilla
2414 to the foreground. */
2415 if (altered &&
2416 (position == nsIWindowMediator::zLevelTop ||
2417 (position == nsIWindowMediator::zLevelBelow && aReqBelow == 0)))
2418 PlaceWindowLayersBehind(zLevel + 1, nsIAppWindow::highestZ, 0);
2420 if (*aPlacement != nsWindowZBottom &&
2421 position == nsIWindowMediator::zLevelBottom)
2422 altered = true;
2423 if (altered || aImmediate) {
2424 if (newPosition == nsIWindowMediator::zLevelTop)
2425 *aPlacement = nsWindowZTop;
2426 else if (newPosition == nsIWindowMediator::zLevelBottom)
2427 *aPlacement = nsWindowZBottom;
2428 else
2429 *aPlacement = nsWindowZRelative;
2431 if (aImmediate) {
2432 nsCOMPtr<nsIBaseWindow> ourBase = do_QueryObject(this);
2433 if (ourBase) {
2434 nsCOMPtr<nsIWidget> ourWidget;
2435 ourBase->GetMainWidget(getter_AddRefs(ourWidget));
2436 ourWidget->PlaceBehind(*aPlacement == nsWindowZBottom
2437 ? eZPlacementBottom
2438 : eZPlacementBelow,
2439 *aActualBelow, false);
2444 /* CalculateZPosition can tell us to be below nothing, because it tries
2445 not to change something it doesn't recognize. A request to verify
2446 being below an unrecognized window, then, is treated as a request
2447 to come to the top (below null) */
2448 nsCOMPtr<nsIAppWindow> windowAbove;
2449 if (newPosition == nsIWindowMediator::zLevelBelow && *aActualBelow) {
2450 windowAbove = (*aActualBelow)->GetWidgetListener()->GetAppWindow();
2453 mediator->SetZPosition(us, newPosition, windowAbove);
2456 return altered;
2459 /* Re-z-position all windows in the layers from aLowLevel to aHighLevel,
2460 inclusive, to be behind aBehind. aBehind of null means on top.
2461 Note this method actually does nothing to our relative window positions.
2462 (And therefore there's no need to inform WindowMediator we're moving
2463 things, because we aren't.) This method is useful for, say, moving
2464 a range of layers of our own windows relative to windows belonging to
2465 external applications.
2467 void AppWindow::PlaceWindowLayersBehind(uint32_t aLowLevel, uint32_t aHighLevel,
2468 nsIAppWindow* aBehind) {
2469 // step through windows in z-order from top to bottommost window
2471 nsCOMPtr<nsIWindowMediator> mediator(
2472 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
2473 if (!mediator) return;
2475 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
2476 mediator->GetZOrderAppWindowEnumerator(0, true,
2477 getter_AddRefs(windowEnumerator));
2478 if (!windowEnumerator) return;
2480 // each window will be moved behind previousHighWidget, itself
2481 // a moving target. initialize it.
2482 nsCOMPtr<nsIWidget> previousHighWidget;
2483 if (aBehind) {
2484 nsCOMPtr<nsIBaseWindow> highBase(do_QueryInterface(aBehind));
2485 if (highBase) highBase->GetMainWidget(getter_AddRefs(previousHighWidget));
2488 // get next lower window
2489 bool more;
2490 while (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)) && more) {
2491 uint32_t nextZ; // z-level of nextWindow
2492 nsCOMPtr<nsISupports> nextWindow;
2493 windowEnumerator->GetNext(getter_AddRefs(nextWindow));
2494 nsCOMPtr<nsIAppWindow> nextAppWindow(do_QueryInterface(nextWindow));
2495 nextAppWindow->GetZLevel(&nextZ);
2496 if (nextZ < aLowLevel)
2497 break; // we've processed all windows through aLowLevel
2499 // move it just below its next higher window
2500 nsCOMPtr<nsIBaseWindow> nextBase(do_QueryInterface(nextAppWindow));
2501 if (nextBase) {
2502 nsCOMPtr<nsIWidget> nextWidget;
2503 nextBase->GetMainWidget(getter_AddRefs(nextWidget));
2504 if (nextZ <= aHighLevel)
2505 nextWidget->PlaceBehind(eZPlacementBelow, previousHighWidget, false);
2506 previousHighWidget = nextWidget;
2511 void AppWindow::SetContentScrollbarVisibility(bool aVisible) {
2512 nsCOMPtr<nsPIDOMWindowOuter> contentWin(
2513 do_GetInterface(mPrimaryContentShell));
2514 if (!contentWin) {
2515 return;
2518 nsContentUtils::SetScrollbarsVisibility(contentWin->GetDocShell(), aVisible);
2521 void AppWindow::ApplyChromeFlags() {
2522 nsCOMPtr<dom::Element> window = GetWindowDOMElement();
2523 if (!window) {
2524 return;
2527 if (mChromeLoaded) {
2528 // The two calls in this block don't need to happen early because they
2529 // don't cause a global restyle on the document. Not only that, but the
2530 // scrollbar stuff needs a content area to toggle the scrollbars on anyway.
2531 // So just don't do these until mChromeLoaded is true.
2533 // Scrollbars have their own special treatment.
2534 SetContentScrollbarVisibility(mChromeFlags &
2535 nsIWebBrowserChrome::CHROME_SCROLLBARS);
2538 /* the other flags are handled together. we have style rules
2539 in navigator.css that trigger visibility based on
2540 the 'chromehidden' attribute of the <window> tag. */
2541 nsAutoString newvalue;
2543 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_MENUBAR))
2544 newvalue.AppendLiteral("menubar ");
2546 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_TOOLBAR))
2547 newvalue.AppendLiteral("toolbar ");
2549 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_LOCATIONBAR))
2550 newvalue.AppendLiteral("location ");
2552 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR))
2553 newvalue.AppendLiteral("directories ");
2555 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_STATUSBAR))
2556 newvalue.AppendLiteral("status ");
2558 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_EXTRA))
2559 newvalue.AppendLiteral("extrachrome ");
2561 // Note that if we're not actually changing the value this will be a no-op,
2562 // so no need to compare to the old value.
2563 IgnoredErrorResult rv;
2564 window->SetAttribute(u"chromehidden"_ns, newvalue, rv);
2567 NS_IMETHODIMP
2568 AppWindow::BeforeStartLayout() {
2569 ApplyChromeFlags();
2570 // Ordering here is important, loading width/height values in
2571 // LoadPersistentWindowState() depends on the chromemargin attribute (since
2572 // we need to translate outer to inner sizes).
2573 SyncAttributesToWidget();
2574 LoadPersistentWindowState();
2575 if (mWindow) {
2576 SizeShell();
2578 return NS_OK;
2581 NS_IMETHODIMP
2582 AppWindow::LockAspectRatio(bool aShouldLock) {
2583 mWindow->LockAspectRatio(aShouldLock);
2584 return NS_OK;
2587 NS_IMETHODIMP
2588 AppWindow::NeedFastSnaphot() {
2589 MOZ_ASSERT(mWindow);
2590 if (!mWindow) {
2591 return NS_ERROR_FAILURE;
2593 mWindow->SetNeedFastSnaphot();
2594 return NS_OK;
2597 void AppWindow::LoadPersistentWindowState() {
2598 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
2599 if (!docShellElement) {
2600 return;
2603 // Check if the window wants to persist anything.
2604 nsAutoString persist;
2605 docShellElement->GetAttr(nsGkAtoms::persist, persist);
2606 if (persist.IsEmpty()) {
2607 return;
2610 auto loadValue = [&](nsAtom* aAttr) {
2611 nsDependentAtomString attrString(aAttr);
2612 if (persist.Find(attrString) >= 0) {
2613 nsAutoString value;
2614 nsresult rv = GetPersistentValue(aAttr, value);
2615 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get persistent state.");
2616 if (NS_SUCCEEDED(rv) && !value.IsEmpty()) {
2617 docShellElement->SetAttr(aAttr, value, IgnoreErrors());
2622 loadValue(nsGkAtoms::screenX);
2623 loadValue(nsGkAtoms::screenY);
2624 loadValue(nsGkAtoms::width);
2625 loadValue(nsGkAtoms::height);
2626 loadValue(nsGkAtoms::sizemode);
2629 void AppWindow::IntrinsicallySizeShell(const CSSIntSize& aWindowDiff,
2630 int32_t& aSpecWidth,
2631 int32_t& aSpecHeight) {
2632 nsCOMPtr<nsIDocumentViewer> viewer;
2633 mDocShell->GetDocViewer(getter_AddRefs(viewer));
2634 if (!viewer) {
2635 return;
2637 RefPtr<nsDocShell> docShell = mDocShell;
2639 CSSIntCoord maxWidth = 0;
2640 CSSIntCoord maxHeight = 0;
2641 CSSIntCoord prefWidth = 0;
2642 if (RefPtr element = GetWindowDOMElement()) {
2643 nsAutoString prefWidthAttr;
2644 if (element->GetAttr(nsGkAtoms::prefwidth, prefWidthAttr)) {
2645 // TODO: Make this more generic perhaps?
2646 if (prefWidthAttr.EqualsLiteral("min-width")) {
2647 if (auto* f = element->GetPrimaryFrame(FlushType::Frames)) {
2648 const auto& coord = f->StylePosition()->mMinWidth;
2649 if (coord.ConvertsToLength()) {
2650 prefWidth = CSSPixel::FromAppUnitsRounded(coord.ToLength());
2657 Maybe<CSSIntSize> size =
2658 viewer->GetContentSize(maxWidth, maxHeight, prefWidth);
2659 if (!size) {
2660 return;
2662 nsPresContext* pc = viewer->GetPresContext();
2663 MOZ_ASSERT(pc, "Should have pres context");
2665 int32_t width = pc->CSSPixelsToDevPixels(size->width);
2666 int32_t height = pc->CSSPixelsToDevPixels(size->height);
2667 SizeShellTo(docShell, width, height);
2669 // Update specified size for the final LoadPositionFromXUL call.
2670 aSpecWidth = size->width + aWindowDiff.width;
2671 aSpecHeight = size->height + aWindowDiff.height;
2674 void AppWindow::SizeShell() {
2675 AutoRestore<bool> sizingShellFromXUL(mSizingShellFromXUL);
2676 mSizingShellFromXUL = true;
2678 int32_t specWidth = -1, specHeight = -1;
2679 bool gotSize = false;
2681 nsAutoString windowType;
2682 if (nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement()) {
2683 windowElement->GetAttr(nsGkAtoms::windowtype, windowType);
2686 const CSSIntSize windowDiff = GetOuterToInnerSizeDifferenceInCSSPixels(
2687 mWindow, UnscaledDevicePixelsPerCSSPixel());
2689 // If we're using fingerprint resistance, we're going to resize the window
2690 // once we have primary content.
2691 if (nsContentUtils::ShouldResistFingerprinting(
2692 "if RFP is enabled we want to round the dimensions of the new"
2693 "new pop up window regardless of their origin",
2694 RFPTarget::RoundWindowSize) &&
2695 windowType.EqualsLiteral("navigator:browser")) {
2696 // Once we've got primary content, force dimensions.
2697 if (mPrimaryContentShell || mPrimaryBrowserParent) {
2698 ForceRoundedDimensions();
2700 // Always avoid setting size/sizemode on this window.
2701 mIgnoreXULSize = true;
2702 mIgnoreXULSizeMode = true;
2703 } else if (!mIgnoreXULSize) {
2704 gotSize = LoadSizeFromXUL(specWidth, specHeight);
2705 specWidth += windowDiff.width;
2706 specHeight += windowDiff.height;
2709 bool positionSet = !mIgnoreXULPosition;
2710 nsCOMPtr<nsIAppWindow> parentWindow(do_QueryReferent(mParentWindow));
2711 #if defined(XP_UNIX) && !defined(XP_MACOSX)
2712 // don't override WM placement on unix for independent, top-level windows
2713 // (however, we think the benefits of intelligent dependent window placement
2714 // trump that override.)
2715 if (!parentWindow) positionSet = false;
2716 #endif
2717 if (positionSet) {
2718 // We have to do this before sizing the window, because sizing depends
2719 // on the resolution of the screen we're on. But positioning needs to
2720 // know the size so that it can constrain to screen bounds.... as an
2721 // initial guess here, we'll use the specified size (if any).
2722 positionSet = LoadPositionFromXUL(specWidth, specHeight);
2725 if (gotSize) {
2726 SetSpecifiedSize(specWidth, specHeight);
2729 // If LoadSizeFromXUL set the size, mIntrinsicallySized will be false.
2730 if (mIntrinsicallySized) {
2731 IntrinsicallySizeShell(windowDiff, specWidth, specHeight);
2734 // Now that we have set the window's final size, we can re-do its
2735 // positioning so that it is properly constrained to the screen.
2736 if (positionSet) {
2737 LoadPositionFromXUL(specWidth, specHeight);
2740 UpdateWindowStateFromMiscXULAttributes();
2742 if (mChromeLoaded && mCenterAfterLoad && !positionSet &&
2743 mWindow->SizeMode() == nsSizeMode_Normal) {
2744 Center(parentWindow, parentWindow ? false : true, false);
2748 NS_IMETHODIMP AppWindow::GetXULBrowserWindow(
2749 nsIXULBrowserWindow** aXULBrowserWindow) {
2750 NS_IF_ADDREF(*aXULBrowserWindow = mXULBrowserWindow);
2751 return NS_OK;
2754 NS_IMETHODIMP AppWindow::SetXULBrowserWindow(
2755 nsIXULBrowserWindow* aXULBrowserWindow) {
2756 mXULBrowserWindow = aXULBrowserWindow;
2757 return NS_OK;
2760 // Given the dimensions of some content area held within this XUL window, and
2761 // assuming that that content area will change its dimensions in linear
2762 // proportion to the dimensions of this XUL window, changes the size of the XUL
2763 // window so that the content area reaches a particular size.
2764 void AppWindow::SizeShellToWithLimit(int32_t aDesiredWidth,
2765 int32_t aDesiredHeight,
2766 int32_t shellItemWidth,
2767 int32_t shellItemHeight) {
2768 int32_t widthDelta = aDesiredWidth - shellItemWidth;
2769 int32_t heightDelta = aDesiredHeight - shellItemHeight;
2771 int32_t winWidth = 0;
2772 int32_t winHeight = 0;
2774 GetSize(&winWidth, &winHeight);
2775 // There's no point in trying to make the window smaller than the
2776 // desired content area size --- that's not likely to work. This whole
2777 // function assumes that the outer docshell is adding some constant
2778 // "border" chrome to the content area.
2779 winWidth = std::max(winWidth + widthDelta, aDesiredWidth);
2780 winHeight = std::max(winHeight + heightDelta, aDesiredHeight);
2782 // Note: Because of the asynchronous resizing on Linux we have to call
2783 // SetSize even when the size doesn't appear to change. A previous call that
2784 // has yet to complete can still change the size. We want the latest call to
2785 // define the final size.
2786 SetSize(winWidth, winHeight, true);
2787 mDominantClientSize = true;
2790 nsresult AppWindow::GetTabCount(uint32_t* aResult) {
2791 if (mXULBrowserWindow) {
2792 return mXULBrowserWindow->GetTabCount(aResult);
2795 *aResult = 0;
2796 return NS_OK;
2799 nsresult AppWindow::GetInitialOpenWindowInfo(
2800 nsIOpenWindowInfo** aOpenWindowInfo) {
2801 NS_ENSURE_ARG_POINTER(aOpenWindowInfo);
2802 *aOpenWindowInfo = do_AddRef(mInitialOpenWindowInfo).take();
2803 return NS_OK;
2806 PresShell* AppWindow::GetPresShell() {
2807 if (!mDocShell) {
2808 return nullptr;
2810 return mDocShell->GetPresShell();
2813 bool AppWindow::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) {
2814 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
2815 if (pm) {
2816 nsCOMPtr<nsPIDOMWindowOuter> window =
2817 mDocShell ? mDocShell->GetWindow() : nullptr;
2818 pm->AdjustPopupsOnWindowChange(window);
2821 // Notify all tabs that the widget moved.
2822 if (mDocShell && mDocShell->GetWindow()) {
2823 nsCOMPtr<EventTarget> eventTarget =
2824 mDocShell->GetWindow()->GetTopWindowRoot();
2825 nsContentUtils::DispatchChromeEvent(
2826 mDocShell->GetDocument(), eventTarget, u"MozUpdateWindowPos"_ns,
2827 CanBubble::eNo, Cancelable::eNo, nullptr);
2830 // Persist position, but not immediately, in case this OS is firing
2831 // repeated move events as the user drags the window
2832 PersistentAttributesDirty(PersistentAttribute::Position, Async);
2833 return false;
2836 bool AppWindow::WindowResized(nsIWidget* aWidget, int32_t aWidth,
2837 int32_t aHeight) {
2838 mDominantClientSize = false;
2839 if (mDocShell) {
2840 mDocShell->SetPositionAndSize(0, 0, aWidth, aHeight, 0);
2842 // Persist size, but not immediately, in case this OS is firing
2843 // repeated size events as the user drags the sizing handle
2844 if (!IsLocked()) {
2845 PersistentAttributesDirty(AllPersistentAttributes(), Async);
2847 // Check if we need to continue a fullscreen change.
2848 switch (mFullscreenChangeState) {
2849 case FullscreenChangeState::WillChange:
2850 mFullscreenChangeState = FullscreenChangeState::WidgetResized;
2851 break;
2852 case FullscreenChangeState::WidgetEnteredFullscreen:
2853 FinishFullscreenChange(true);
2854 break;
2855 case FullscreenChangeState::WidgetExitedFullscreen:
2856 FinishFullscreenChange(false);
2857 break;
2858 case FullscreenChangeState::WidgetResized:
2859 case FullscreenChangeState::NotChanging:
2860 break;
2862 return true;
2865 bool AppWindow::RequestWindowClose(nsIWidget* aWidget) {
2866 // Maintain a reference to this as it is about to get destroyed.
2867 nsCOMPtr<nsIAppWindow> appWindow(this);
2869 nsCOMPtr<nsPIDOMWindowOuter> window(mDocShell ? mDocShell->GetWindow()
2870 : nullptr);
2871 nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window);
2873 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
2874 if (!presShell) {
2875 mozilla::DebugOnly<bool> dying;
2876 MOZ_ASSERT(NS_SUCCEEDED(mDocShell->IsBeingDestroyed(&dying)) && dying,
2877 "No presShell, but window is not being destroyed");
2878 } else if (eventTarget) {
2879 RefPtr<nsPresContext> presContext = presShell->GetPresContext();
2881 nsEventStatus status = nsEventStatus_eIgnore;
2882 WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
2883 if (NS_SUCCEEDED(EventDispatcher::Dispatch(eventTarget, presContext, &event,
2884 nullptr, &status)) &&
2885 status == nsEventStatus_eConsumeNoDefault)
2886 return false;
2889 Destroy();
2890 return false;
2893 void AppWindow::SizeModeChanged(nsSizeMode aSizeMode) {
2894 const bool wasWidgetInFullscreen = mIsWidgetInFullscreen;
2895 // Fullscreen and minimized states are usually compatible, and the widget
2896 // typically returns to fullscreen after restoration. By not updating the
2897 // widget's fullscreen state while it is minimized, we can avoid unnecessary
2898 // fullscreen exits, such as those encountered in bug 1823284.
2899 if (aSizeMode != nsSizeMode_Minimized) {
2900 mIsWidgetInFullscreen = aSizeMode == nsSizeMode_Fullscreen;
2903 const bool fullscreenChanged = wasWidgetInFullscreen != mIsWidgetInFullscreen;
2904 if (fullscreenChanged) {
2905 FullscreenWillChange(mIsWidgetInFullscreen);
2908 // An alwaysRaised (or higher) window will hide any newly opened normal
2909 // browser windows, so here we just drop a raised window to the normal
2910 // zlevel if it's maximized. We make no provision for automatically
2911 // re-raising it when restored.
2912 if (aSizeMode == nsSizeMode_Maximized || aSizeMode == nsSizeMode_Fullscreen) {
2913 uint32_t zLevel;
2914 GetZLevel(&zLevel);
2915 if (zLevel > nsIAppWindow::normalZ) {
2916 SetZLevel(nsIAppWindow::normalZ);
2920 RecomputeBrowsingContextVisibility();
2922 PersistentAttributesDirty(PersistentAttribute::Misc, Sync);
2923 nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
2924 mDocShell ? mDocShell->GetWindow() : nullptr;
2925 if (ourWindow) {
2926 // Always fire a user-defined sizemodechange event on the window
2927 ourWindow->DispatchCustomEvent(u"sizemodechange"_ns);
2930 if (PresShell* presShell = GetPresShell()) {
2931 presShell->GetPresContext()->SizeModeChanged(aSizeMode);
2934 if (fullscreenChanged) {
2935 FullscreenChanged(mIsWidgetInFullscreen);
2938 // Note the current implementation of SetSizeMode just stores
2939 // the new state; it doesn't actually resize. So here we store
2940 // the state and pass the event on to the OS. The day is coming
2941 // when we'll handle the event here, and the return result will
2942 // then need to be different.
2945 void AppWindow::UIResolutionChanged() {
2946 nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
2947 mDocShell ? mDocShell->GetWindow() : nullptr;
2948 if (ourWindow) {
2949 ourWindow->DispatchCustomEvent(u"resolutionchange"_ns,
2950 ChromeOnlyDispatch::eYes);
2954 void AppWindow::FullscreenWillChange(bool aInFullscreen) {
2955 if (mDocShell) {
2956 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2957 ourWindow->FullscreenWillChange(aInFullscreen);
2960 MOZ_ASSERT(mFullscreenChangeState == FullscreenChangeState::NotChanging);
2962 CSSToLayoutDeviceScale scale = UnscaledDevicePixelsPerCSSPixel();
2963 CSSIntSize windowSizeCSS = RoundedToInt(GetSize() / scale);
2965 CSSIntSize screenSizeCSS;
2966 GetAvailScreenSize(&screenSizeCSS.width, &screenSizeCSS.height);
2968 // Check if the window is already at the expected dimensions. If it is, set
2969 // the fullscreen change state to WidgetResized to avoid waiting for a resize
2970 // event. On macOS, a fullscreen window could be slightly higher than
2971 // available screen size because of the OS menu bar isn't yet hidden.
2972 mFullscreenChangeState =
2973 (aInFullscreen == (windowSizeCSS.width == screenSizeCSS.width &&
2974 windowSizeCSS.height >= screenSizeCSS.height))
2975 ? FullscreenChangeState::WidgetResized
2976 : FullscreenChangeState::WillChange;
2979 void AppWindow::FullscreenChanged(bool aInFullscreen) {
2980 if (mFullscreenChangeState == FullscreenChangeState::WidgetResized) {
2981 FinishFullscreenChange(aInFullscreen);
2982 } else {
2983 NS_WARNING_ASSERTION(
2984 mFullscreenChangeState == FullscreenChangeState::WillChange,
2985 "Unexpected fullscreen change state");
2986 FullscreenChangeState newState =
2987 aInFullscreen ? FullscreenChangeState::WidgetEnteredFullscreen
2988 : FullscreenChangeState::WidgetExitedFullscreen;
2989 mFullscreenChangeState = newState;
2990 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
2991 // Wait for resize for a small amount of time.
2992 // 80ms is actually picked arbitrarily. But it shouldn't be too large
2993 // in case the widget resize is not going to happen at all, which can
2994 // be the case for some Linux window managers and possibly Android.
2995 NS_DelayedDispatchToCurrentThread(
2996 NS_NewRunnableFunction(
2997 "AppWindow::FullscreenChanged",
2998 [this, kungFuDeathGrip, newState, aInFullscreen]() {
2999 if (mFullscreenChangeState == newState) {
3000 FinishFullscreenChange(aInFullscreen);
3003 80);
3007 void AppWindow::FinishFullscreenChange(bool aInFullscreen) {
3008 mFullscreenChangeState = FullscreenChangeState::NotChanging;
3009 if (mDocShell) {
3010 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
3011 ourWindow->FinishFullscreenChange(aInFullscreen);
3016 void AppWindow::MacFullscreenMenubarOverlapChanged(
3017 mozilla::DesktopCoord aOverlapAmount) {
3018 if (mDocShell) {
3019 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
3020 ourWindow->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
3025 void AppWindow::RecomputeBrowsingContextVisibility() {
3026 if (!mDocShell) {
3027 return;
3029 RefPtr bc = mDocShell->GetBrowsingContext();
3030 if (!bc) {
3031 return;
3033 bc->Canonical()->RecomputeAppWindowVisibility();
3036 void AppWindow::OcclusionStateChanged(bool aIsFullyOccluded) {
3037 if (!mDocShell) {
3038 return;
3040 RecomputeBrowsingContextVisibility();
3041 if (RefPtr win = mDocShell->GetWindow()) {
3042 // And always fire a user-defined occlusionstatechange event on the window
3043 win->DispatchCustomEvent(u"occlusionstatechange"_ns,
3044 ChromeOnlyDispatch::eYes);
3048 void AppWindow::OSToolbarButtonPressed() {
3049 // Keep a reference as setting the chrome flags can fire events.
3050 nsCOMPtr<nsIAppWindow> appWindow(this);
3052 // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
3053 // due to components with multiple sidebar components
3054 // (such as Mail/News, Addressbook, etc)... and frankly,
3055 // Mac IE, OmniWeb, and other Mac OS X apps all work this way
3056 uint32_t chromeMask = (nsIWebBrowserChrome::CHROME_TOOLBAR |
3057 nsIWebBrowserChrome::CHROME_LOCATIONBAR |
3058 nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
3060 nsCOMPtr<nsIWebBrowserChrome> wbc(do_GetInterface(appWindow));
3061 if (!wbc) return;
3063 uint32_t chromeFlags, newChromeFlags = 0;
3064 wbc->GetChromeFlags(&chromeFlags);
3065 newChromeFlags = chromeFlags & chromeMask;
3066 if (!newChromeFlags)
3067 chromeFlags |= chromeMask;
3068 else
3069 chromeFlags &= (~newChromeFlags);
3070 wbc->SetChromeFlags(chromeFlags);
3073 bool AppWindow::ZLevelChanged(bool aImmediate, nsWindowZ* aPlacement,
3074 nsIWidget* aRequestBelow,
3075 nsIWidget** aActualBelow) {
3076 if (aActualBelow) *aActualBelow = nullptr;
3078 return ConstrainToZLevel(aImmediate, aPlacement, aRequestBelow, aActualBelow);
3081 void AppWindow::WindowActivated() {
3082 nsCOMPtr<nsIAppWindow> appWindow(this);
3084 // focusing the window could cause it to close, so keep a reference to it
3085 if (mDocShell) {
3086 if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
3087 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
3088 fm->WindowRaised(window, nsFocusManager::GenerateFocusActionId());
3093 if (mChromeLoaded) {
3094 PersistentAttributesDirty(AllPersistentAttributes(), Sync);
3098 void AppWindow::WindowDeactivated() {
3099 if (mDocShell) {
3100 if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
3101 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
3102 if (!fm->IsTestMode()) {
3103 fm->WindowLowered(window, nsFocusManager::GenerateFocusActionId());
3110 #ifdef USE_NATIVE_MENUS
3112 struct LoadNativeMenusListener {
3113 LoadNativeMenusListener(Document* aDoc, nsIWidget* aParentWindow)
3114 : mDocument(aDoc), mParentWindow(aParentWindow) {}
3116 RefPtr<Document> mDocument;
3117 nsCOMPtr<nsIWidget> mParentWindow;
3120 static bool sHiddenWindowLoadedNativeMenus = false;
3121 static nsTArray<LoadNativeMenusListener> sLoadNativeMenusListeners;
3123 static void BeginLoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow);
3125 static void LoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
3126 MOZ_ASSERT(!gfxPlatform::IsHeadless());
3128 // Find the menubar tag (if there is more than one, we ignore all but
3129 // the first).
3130 nsCOMPtr<nsINodeList> menubarElements = aDoc->GetElementsByTagNameNS(
3131 nsLiteralString(
3132 u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
3133 u"menubar"_ns);
3135 nsCOMPtr<nsINode> menubarNode;
3136 if (menubarElements) {
3137 menubarNode = menubarElements->Item(0);
3140 using widget::NativeMenuSupport;
3141 if (menubarNode) {
3142 nsCOMPtr<Element> menubarContent(do_QueryInterface(menubarNode));
3143 NativeMenuSupport::CreateNativeMenuBar(aParentWindow, menubarContent);
3144 } else {
3145 NativeMenuSupport::CreateNativeMenuBar(aParentWindow, nullptr);
3148 if (!sHiddenWindowLoadedNativeMenus) {
3149 sHiddenWindowLoadedNativeMenus = true;
3150 for (auto& listener : sLoadNativeMenusListeners) {
3151 BeginLoadNativeMenus(listener.mDocument, listener.mParentWindow);
3153 sLoadNativeMenusListeners.Clear();
3157 class L10nReadyPromiseHandler final : public dom::PromiseNativeHandler {
3158 public:
3159 NS_DECL_ISUPPORTS
3161 L10nReadyPromiseHandler(Document* aDoc, nsIWidget* aParentWindow)
3162 : mDocument(aDoc), mWindow(aParentWindow) {}
3164 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
3165 ErrorResult& aRv) override {
3166 LoadNativeMenus(mDocument, mWindow);
3169 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
3170 ErrorResult& aRv) override {
3171 // Again, this shouldn't happen, but fallback to loading the menus as is.
3172 NS_WARNING(
3173 "L10nReadyPromiseHandler rejected - loading fallback native "
3174 "menu.");
3175 LoadNativeMenus(mDocument, mWindow);
3178 private:
3179 ~L10nReadyPromiseHandler() = default;
3181 RefPtr<Document> mDocument;
3182 nsCOMPtr<nsIWidget> mWindow;
3185 NS_IMPL_ISUPPORTS0(L10nReadyPromiseHandler)
3187 static void BeginLoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
3188 RefPtr<DocumentL10n> l10n = aDoc->GetL10n();
3189 if (l10n) {
3190 // Wait for l10n to be ready so the menus are localized.
3191 RefPtr<Promise> promise = l10n->Ready();
3192 MOZ_ASSERT(promise);
3193 RefPtr<L10nReadyPromiseHandler> handler =
3194 new L10nReadyPromiseHandler(aDoc, aParentWindow);
3195 promise->AppendNativeHandler(handler);
3196 } else {
3197 // Something went wrong loading the doc and l10n wasn't created. This
3198 // shouldn't really happen, but if it does fallback to trying to load
3199 // the menus as is.
3200 LoadNativeMenus(aDoc, aParentWindow);
3204 #endif
3206 class AppWindowTimerCallback final : public nsITimerCallback, public nsINamed {
3207 public:
3208 explicit AppWindowTimerCallback(AppWindow* aWindow) : mWindow(aWindow) {}
3210 NS_DECL_THREADSAFE_ISUPPORTS
3212 NS_IMETHOD Notify(nsITimer* aTimer) override {
3213 // Although this object participates in a refcount cycle (this -> mWindow
3214 // -> mSPTimer -> this), mSPTimer is a one-shot timer and releases this
3215 // after it fires. So we don't need to release mWindow here.
3217 mWindow->FirePersistenceTimer();
3218 return NS_OK;
3221 NS_IMETHOD GetName(nsACString& aName) override {
3222 aName.AssignLiteral("AppWindowTimerCallback");
3223 return NS_OK;
3226 private:
3227 ~AppWindowTimerCallback() {}
3229 RefPtr<AppWindow> mWindow;
3232 NS_IMPL_ISUPPORTS(AppWindowTimerCallback, nsITimerCallback, nsINamed)
3234 void AppWindow::PersistentAttributesDirty(PersistentAttributes aAttributes,
3235 PersistentAttributeUpdate aUpdate) {
3236 aAttributes = aAttributes & mPersistentAttributesMask;
3237 if (aAttributes.isEmpty()) {
3238 return;
3241 mPersistentAttributesDirty += aAttributes;
3242 if (aUpdate == Sync) {
3243 // Only apply the attributes we've been requested to apply sync, not other
3244 // potentially dirty attributes that have been requested asynchronously.
3245 SavePersistentAttributes(aAttributes);
3246 return;
3248 if (!mSPTimer) {
3249 mSPTimer = NS_NewTimer();
3250 if (!mSPTimer) {
3251 NS_WARNING("Couldn't create timer instance?");
3252 return;
3256 RefPtr<AppWindowTimerCallback> callback = new AppWindowTimerCallback(this);
3257 mSPTimer->InitWithCallback(callback, SIZE_PERSISTENCE_TIMEOUT,
3258 nsITimer::TYPE_ONE_SHOT);
3261 void AppWindow::FirePersistenceTimer() { SavePersistentAttributes(); }
3263 //----------------------------------------
3264 // nsIWebProgessListener implementation
3265 //----------------------------------------
3266 NS_IMETHODIMP
3267 AppWindow::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3268 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
3269 int32_t aCurTotalProgress,
3270 int32_t aMaxTotalProgress) {
3271 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3272 return NS_OK;
3275 NS_IMETHODIMP
3276 AppWindow::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3277 uint32_t aStateFlags, nsresult aStatus) {
3278 // If the notification is not about a document finishing, then just
3279 // ignore it...
3280 if (!(aStateFlags & nsIWebProgressListener::STATE_STOP) ||
3281 !(aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) {
3282 return NS_OK;
3285 if (mChromeLoaded) return NS_OK;
3287 // If this document notification is for a frame then ignore it...
3288 nsCOMPtr<mozIDOMWindowProxy> eventWin;
3289 aProgress->GetDOMWindow(getter_AddRefs(eventWin));
3290 auto* eventPWin = nsPIDOMWindowOuter::From(eventWin);
3291 if (eventPWin) {
3292 nsPIDOMWindowOuter* rootPWin = eventPWin->GetPrivateRoot();
3293 if (eventPWin != rootPWin) return NS_OK;
3296 mChromeLoaded = true;
3297 mLockedUntilChromeLoad = false;
3299 #ifdef USE_NATIVE_MENUS
3300 ///////////////////////////////
3301 // Find the Menubar DOM and Load the menus, hooking them up to the loaded
3302 // commands
3303 ///////////////////////////////
3304 if (!gfxPlatform::IsHeadless()) {
3305 nsCOMPtr<nsIDocumentViewer> viewer;
3306 mDocShell->GetDocViewer(getter_AddRefs(viewer));
3307 if (viewer) {
3308 RefPtr<Document> menubarDoc = viewer->GetDocument();
3309 if (menubarDoc) {
3310 if (mIsHiddenWindow || sHiddenWindowLoadedNativeMenus) {
3311 BeginLoadNativeMenus(menubarDoc, mWindow);
3312 } else {
3313 sLoadNativeMenusListeners.EmplaceBack(menubarDoc, mWindow);
3318 #endif // USE_NATIVE_MENUS
3320 OnChromeLoaded();
3322 return NS_OK;
3325 NS_IMETHODIMP
3326 AppWindow::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3327 nsIURI* aURI, uint32_t aFlags) {
3328 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3329 return NS_OK;
3332 NS_IMETHODIMP
3333 AppWindow::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3334 nsresult aStatus, const char16_t* aMessage) {
3335 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3336 return NS_OK;
3339 NS_IMETHODIMP
3340 AppWindow::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3341 uint32_t aState) {
3342 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3343 return NS_OK;
3346 NS_IMETHODIMP
3347 AppWindow::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
3348 nsIRequest* aRequest, uint32_t aEvent) {
3349 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3350 return NS_OK;
3354 * ExecuteCloseHandler - Run the close handler, if any.
3355 * @return true iff we found a close handler to run.
3357 bool AppWindow::ExecuteCloseHandler() {
3358 /* If the event handler closes this window -- a likely scenario --
3359 things get deleted out of order without this death grip.
3360 (The problem may be the death grip in nsWindow::windowProc,
3361 which forces this window's widget to remain alive longer
3362 than it otherwise would.) */
3363 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
3365 nsCOMPtr<EventTarget> eventTarget;
3366 if (mDocShell) {
3367 eventTarget = do_QueryInterface(mDocShell->GetWindow());
3370 if (eventTarget) {
3371 nsCOMPtr<nsIDocumentViewer> viewer;
3372 mDocShell->GetDocViewer(getter_AddRefs(viewer));
3373 if (viewer) {
3374 RefPtr<nsPresContext> presContext = viewer->GetPresContext();
3376 nsEventStatus status = nsEventStatus_eIgnore;
3377 WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
3379 nsresult rv = EventDispatcher::Dispatch(eventTarget, presContext, &event,
3380 nullptr, &status);
3381 if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault)
3382 return true;
3383 // else fall through and return false
3387 return false;
3388 } // ExecuteCloseHandler
3390 void AppWindow::ConstrainToOpenerScreen(int32_t* aX, int32_t* aY) {
3391 if (mOpenerScreenRect.IsEmpty()) {
3392 *aX = *aY = 0;
3393 return;
3396 int32_t left, top, width, height;
3397 // Constrain initial positions to the same screen as opener
3398 nsCOMPtr<nsIScreenManager> screenmgr =
3399 do_GetService("@mozilla.org/gfx/screenmanager;1");
3400 if (screenmgr) {
3401 nsCOMPtr<nsIScreen> screen = screenmgr->ScreenForRect(mOpenerScreenRect);
3402 if (screen) {
3403 screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
3404 if (*aX < left || *aX > left + width) {
3405 *aX = left;
3407 if (*aY < top || *aY > top + height) {
3408 *aY = top;
3414 nsIAppWindow* AppWindow::WidgetListenerDelegate::GetAppWindow() {
3415 return mAppWindow->GetAppWindow();
3418 PresShell* AppWindow::WidgetListenerDelegate::GetPresShell() {
3419 return mAppWindow->GetPresShell();
3422 bool AppWindow::WidgetListenerDelegate::WindowMoved(nsIWidget* aWidget,
3423 int32_t aX, int32_t aY,
3424 ByMoveToRect) {
3425 RefPtr<AppWindow> holder = mAppWindow;
3426 return holder->WindowMoved(aWidget, aX, aY);
3429 bool AppWindow::WidgetListenerDelegate::WindowResized(nsIWidget* aWidget,
3430 int32_t aWidth,
3431 int32_t aHeight) {
3432 RefPtr<AppWindow> holder = mAppWindow;
3433 return holder->WindowResized(aWidget, aWidth, aHeight);
3436 bool AppWindow::WidgetListenerDelegate::RequestWindowClose(nsIWidget* aWidget) {
3437 RefPtr<AppWindow> holder = mAppWindow;
3438 return holder->RequestWindowClose(aWidget);
3441 void AppWindow::WidgetListenerDelegate::SizeModeChanged(nsSizeMode aSizeMode) {
3442 RefPtr<AppWindow> holder = mAppWindow;
3443 holder->SizeModeChanged(aSizeMode);
3446 void AppWindow::WidgetListenerDelegate::UIResolutionChanged() {
3447 RefPtr<AppWindow> holder = mAppWindow;
3448 holder->UIResolutionChanged();
3451 void AppWindow::WidgetListenerDelegate::MacFullscreenMenubarOverlapChanged(
3452 DesktopCoord aOverlapAmount) {
3453 RefPtr<AppWindow> holder = mAppWindow;
3454 return holder->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
3457 void AppWindow::WidgetListenerDelegate::OcclusionStateChanged(
3458 bool aIsFullyOccluded) {
3459 RefPtr<AppWindow> holder = mAppWindow;
3460 holder->OcclusionStateChanged(aIsFullyOccluded);
3463 void AppWindow::WidgetListenerDelegate::OSToolbarButtonPressed() {
3464 RefPtr<AppWindow> holder = mAppWindow;
3465 holder->OSToolbarButtonPressed();
3468 bool AppWindow::WidgetListenerDelegate::ZLevelChanged(
3469 bool aImmediate, nsWindowZ* aPlacement, nsIWidget* aRequestBelow,
3470 nsIWidget** aActualBelow) {
3471 RefPtr<AppWindow> holder = mAppWindow;
3472 return holder->ZLevelChanged(aImmediate, aPlacement, aRequestBelow,
3473 aActualBelow);
3476 void AppWindow::WidgetListenerDelegate::WindowActivated() {
3477 RefPtr<AppWindow> holder = mAppWindow;
3478 holder->WindowActivated();
3481 void AppWindow::WidgetListenerDelegate::WindowDeactivated() {
3482 RefPtr<AppWindow> holder = mAppWindow;
3483 holder->WindowDeactivated();
3486 } // namespace mozilla