Bug 1728955: part 8) Refactor `DisplayErrCode` in Windows' `nsClipboard`. r=masayuki
[gecko.git] / xpfe / appshell / AppWindow.cpp
blob6da60627646f2f15783c2bb086f86c1d6aa78749
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 ci et: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/MathAlgorithms.h"
9 // Local includes
10 #include "AppWindow.h"
11 #include <algorithm>
13 // Helper classes
14 #include "nsPrintfCString.h"
15 #include "nsString.h"
16 #include "nsWidgetsCID.h"
17 #include "nsThreadUtils.h"
18 #include "nsNetCID.h"
19 #include "nsQueryObject.h"
20 #include "mozilla/ProfilerLabels.h"
21 #include "mozilla/Sprintf.h"
23 // Interfaces needed to be included
24 #include "nsGlobalWindowOuter.h"
25 #include "nsIAppShell.h"
26 #include "nsIAppShellService.h"
27 #include "nsIContentViewer.h"
28 #include "mozilla/dom/Document.h"
29 #include "nsPIDOMWindow.h"
30 #include "nsScreen.h"
31 #include "nsIEmbeddingSiteWindow.h"
32 #include "nsIInterfaceRequestor.h"
33 #include "nsIInterfaceRequestorUtils.h"
34 #include "nsIIOService.h"
35 #include "nsIObserverService.h"
36 #include "nsIOpenWindowInfo.h"
37 #include "nsIWindowMediator.h"
38 #include "nsIScreenManager.h"
39 #include "nsIScreen.h"
40 #include "nsIWindowWatcher.h"
41 #include "nsIURI.h"
42 #include "nsAppShellCID.h"
43 #include "nsReadableUtils.h"
44 #include "nsStyleConsts.h"
45 #include "nsPresContext.h"
46 #include "nsContentUtils.h"
47 #include "nsGlobalWindow.h"
48 #include "nsXULTooltipListener.h"
49 #include "nsXULPopupManager.h"
50 #include "nsFocusManager.h"
51 #include "nsContentList.h"
52 #include "nsIDOMWindowUtils.h"
53 #include "nsServiceManagerUtils.h"
55 #include "prenv.h"
56 #include "mozilla/AutoRestore.h"
57 #include "mozilla/Preferences.h"
58 #include "mozilla/PresShell.h"
59 #include "mozilla/Services.h"
60 #include "mozilla/SpinEventLoopUntil.h"
61 #include "mozilla/dom/BarProps.h"
62 #include "mozilla/dom/DOMRect.h"
63 #include "mozilla/dom/Element.h"
64 #include "mozilla/dom/Event.h"
65 #include "mozilla/dom/ScriptSettings.h"
66 #include "mozilla/dom/BrowserHost.h"
67 #include "mozilla/dom/BrowserParent.h"
68 #include "mozilla/dom/LoadURIOptionsBinding.h"
69 #include "mozilla/intl/LocaleService.h"
70 #include "mozilla/EventDispatcher.h"
72 #ifdef XP_WIN
73 # include "mozilla/PreXULSkeletonUI.h"
74 # include "nsIWindowsUIUtils.h"
75 #endif
77 #ifdef MOZ_NEW_XULSTORE
78 # include "mozilla/XULStore.h"
79 #endif
81 #include "mozilla/dom/DocumentL10n.h"
83 #ifdef XP_MACOSX
84 # include "mozilla/widget/NativeMenuSupport.h"
85 # define USE_NATIVE_MENUS
86 #endif
88 #define SIZEMODE_NORMAL u"normal"_ns
89 #define SIZEMODE_MAXIMIZED u"maximized"_ns
90 #define SIZEMODE_MINIMIZED u"minimized"_ns
91 #define SIZEMODE_FULLSCREEN u"fullscreen"_ns
93 #define WINDOWTYPE_ATTRIBUTE u"windowtype"_ns
95 #define PERSIST_ATTRIBUTE u"persist"_ns
96 #define SCREENX_ATTRIBUTE u"screenX"_ns
97 #define SCREENY_ATTRIBUTE u"screenY"_ns
98 #define WIDTH_ATTRIBUTE u"width"_ns
99 #define HEIGHT_ATTRIBUTE u"height"_ns
100 #define MODE_ATTRIBUTE u"sizemode"_ns
101 #define TILED_ATTRIBUTE u"gtktiledwindow"_ns
102 #define ZLEVEL_ATTRIBUTE u"zlevel"_ns
104 #define SIZE_PERSISTENCE_TIMEOUT 500 // msec
106 //*****************************************************************************
107 //*** AppWindow: Object Management
108 //*****************************************************************************
110 namespace mozilla {
112 using dom::AutoNoJSAPI;
113 using dom::BrowserHost;
114 using dom::BrowsingContext;
115 using dom::Document;
116 using dom::DocumentL10n;
117 using dom::Element;
118 using dom::EventTarget;
119 using dom::LoadURIOptions;
120 using dom::Promise;
122 AppWindow::AppWindow(uint32_t aChromeFlags)
123 : mChromeTreeOwner(nullptr),
124 mContentTreeOwner(nullptr),
125 mPrimaryContentTreeOwner(nullptr),
126 mModalStatus(NS_OK),
127 mFullscreenChangeState(FullscreenChangeState::NotChanging),
128 mContinueModalLoop(false),
129 mDebuting(false),
130 mChromeLoaded(false),
131 mSizingShellFromXUL(false),
132 mShowAfterLoad(false),
133 mIntrinsicallySized(false),
134 mCenterAfterLoad(false),
135 mIsHiddenWindow(false),
136 mLockedUntilChromeLoad(false),
137 mIgnoreXULSize(false),
138 mIgnoreXULPosition(false),
139 mChromeFlagsFrozen(false),
140 mIgnoreXULSizeMode(false),
141 mDestroying(false),
142 mRegistered(false),
143 mPersistentAttributesDirty(0),
144 mPersistentAttributesMask(0),
145 mChromeFlags(aChromeFlags),
146 mSPTimerLock("AppWindow.mSPTimerLock"),
147 mWidgetListenerDelegate(this) {}
149 AppWindow::~AppWindow() {
151 MutexAutoLock lock(mSPTimerLock);
152 if (mSPTimer) mSPTimer->Cancel();
154 Destroy();
157 //*****************************************************************************
158 // AppWindow::nsISupports
159 //*****************************************************************************
161 NS_IMPL_ADDREF(AppWindow)
162 NS_IMPL_RELEASE(AppWindow)
164 NS_INTERFACE_MAP_BEGIN(AppWindow)
165 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAppWindow)
166 NS_INTERFACE_MAP_ENTRY(nsIAppWindow)
167 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
168 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
169 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
170 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
171 NS_INTERFACE_MAP_ENTRY_CONCRETE(AppWindow)
172 NS_INTERFACE_MAP_END
174 nsresult AppWindow::Initialize(nsIAppWindow* aParent, nsIAppWindow* aOpener,
175 int32_t aInitialWidth, int32_t aInitialHeight,
176 bool aIsHiddenWindow,
177 nsWidgetInitData& widgetInitData) {
178 nsresult rv;
179 nsCOMPtr<nsIWidget> parentWidget;
181 mIsHiddenWindow = aIsHiddenWindow;
183 int32_t initialX = 0, initialY = 0;
184 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aOpener));
185 if (base) {
186 int32_t x, y, width, height;
187 rv = base->GetPositionAndSize(&x, &y, &width, &height);
188 if (NS_FAILED(rv)) {
189 mOpenerScreenRect.SetEmpty();
190 } else {
191 double scale;
192 if (NS_SUCCEEDED(base->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
193 mOpenerScreenRect.SetRect(
194 NSToIntRound(x / scale), NSToIntRound(y / scale),
195 NSToIntRound(width / scale), NSToIntRound(height / scale));
196 } else {
197 mOpenerScreenRect.SetRect(x, y, width, height);
199 initialX = mOpenerScreenRect.X();
200 initialY = mOpenerScreenRect.Y();
201 ConstrainToOpenerScreen(&initialX, &initialY);
205 // XXX: need to get the default window size from prefs...
206 // Doesn't come from prefs... will come from CSS/XUL/RDF
207 DesktopIntRect deskRect(initialX, initialY, aInitialWidth, aInitialHeight);
209 // Create top level window
210 if (gfxPlatform::IsHeadless()) {
211 mWindow = nsIWidget::CreateHeadlessWidget();
212 } else {
213 mWindow = nsIWidget::CreateTopLevelWindow();
215 if (!mWindow) {
216 return NS_ERROR_FAILURE;
219 /* This next bit is troublesome. We carry two different versions of a pointer
220 to our parent window. One is the parent window's widget, which is passed
221 to our own widget. The other is a weak reference we keep here to our
222 parent AppWindow. The former is useful to the widget, and we can't
223 trust its treatment of the parent reference because they're platform-
224 specific. The latter is useful to this class.
225 A better implementation would be one in which the parent keeps strong
226 references to its children and closes them before it allows itself
227 to be closed. This would mimic the behaviour of OSes that support
228 top-level child windows in OSes that do not. Later.
230 nsCOMPtr<nsIBaseWindow> parentAsWin(do_QueryInterface(aParent));
231 if (parentAsWin) {
232 parentAsWin->GetMainWidget(getter_AddRefs(parentWidget));
233 mParentWindow = do_GetWeakReference(aParent);
236 mWindow->SetWidgetListener(&mWidgetListenerDelegate);
237 rv = mWindow->Create((nsIWidget*)parentWidget, // Parent nsIWidget
238 nullptr, // Native parent widget
239 deskRect, // Widget dimensions
240 &widgetInitData); // Widget initialization data
241 NS_ENSURE_SUCCESS(rv, rv);
243 LayoutDeviceIntRect r = mWindow->GetClientBounds();
244 // Match the default background color of content. Important on windows
245 // since we no longer use content child widgets.
246 mWindow->SetBackgroundColor(NS_RGB(255, 255, 255));
248 // All Chrome BCs exist within the same BrowsingContextGroup, so we don't need
249 // to pass in the opener window here. The opener is set later, if needed, by
250 // nsWindowWatcher.
251 RefPtr<BrowsingContext> browsingContext =
252 BrowsingContext::CreateIndependent(BrowsingContext::Type::Chrome);
254 // Create web shell
255 mDocShell = nsDocShell::Create(browsingContext);
256 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
258 // Make sure to set the item type on the docshell _before_ calling
259 // InitWindow() so it knows what type it is.
260 NS_ENSURE_SUCCESS(EnsureChromeTreeOwner(), NS_ERROR_FAILURE);
262 mDocShell->SetTreeOwner(mChromeTreeOwner);
264 r.MoveTo(0, 0);
265 NS_ENSURE_SUCCESS(mDocShell->InitWindow(nullptr, mWindow, r.X(), r.Y(),
266 r.Width(), r.Height()),
267 NS_ERROR_FAILURE);
269 // Attach a WebProgress listener.during initialization...
270 mDocShell->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK);
272 mWindow->MaybeDispatchInitialFocusEvent();
274 return rv;
277 //*****************************************************************************
278 // AppWindow::nsIIntefaceRequestor
279 //*****************************************************************************
281 NS_IMETHODIMP AppWindow::GetInterface(const nsIID& aIID, void** aSink) {
282 nsresult rv;
284 NS_ENSURE_ARG_POINTER(aSink);
286 if (aIID.Equals(NS_GET_IID(nsIPrompt))) {
287 rv = EnsurePrompter();
288 if (NS_FAILED(rv)) return rv;
289 return mPrompter->QueryInterface(aIID, aSink);
291 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
292 rv = EnsureAuthPrompter();
293 if (NS_FAILED(rv)) return rv;
294 return mAuthPrompter->QueryInterface(aIID, aSink);
296 if (aIID.Equals(NS_GET_IID(mozIDOMWindowProxy))) {
297 return GetWindowDOMWindow(reinterpret_cast<mozIDOMWindowProxy**>(aSink));
299 if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) {
300 nsCOMPtr<mozIDOMWindowProxy> window = nullptr;
301 rv = GetWindowDOMWindow(getter_AddRefs(window));
302 nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(window);
303 domWindow.forget(aSink);
304 return rv;
306 if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome)) &&
307 NS_SUCCEEDED(EnsureContentTreeOwner()) &&
308 NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink)))
309 return NS_OK;
311 if (aIID.Equals(NS_GET_IID(nsIEmbeddingSiteWindow)) &&
312 NS_SUCCEEDED(EnsureContentTreeOwner()) &&
313 NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink)))
314 return NS_OK;
316 return QueryInterface(aIID, aSink);
319 //*****************************************************************************
320 // AppWindow::nsIAppWindow
321 //*****************************************************************************
323 NS_IMETHODIMP AppWindow::GetDocShell(nsIDocShell** aDocShell) {
324 NS_ENSURE_ARG_POINTER(aDocShell);
326 *aDocShell = mDocShell;
327 NS_IF_ADDREF(*aDocShell);
328 return NS_OK;
331 NS_IMETHODIMP AppWindow::GetZLevel(uint32_t* outLevel) {
332 nsCOMPtr<nsIWindowMediator> mediator(
333 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
334 if (mediator)
335 mediator->GetZLevel(this, outLevel);
336 else
337 *outLevel = normalZ;
338 return NS_OK;
341 NS_IMETHODIMP AppWindow::SetZLevel(uint32_t aLevel) {
342 nsCOMPtr<nsIWindowMediator> mediator(
343 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
344 if (!mediator) return NS_ERROR_FAILURE;
346 uint32_t zLevel;
347 mediator->GetZLevel(this, &zLevel);
348 if (zLevel == aLevel) return NS_OK;
350 /* refuse to raise a maximized window above the normal browser level,
351 for fear it could hide newly opened browser windows */
352 if (aLevel > nsIAppWindow::normalZ && mWindow) {
353 nsSizeMode sizeMode = mWindow->SizeMode();
354 if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) {
355 return NS_ERROR_FAILURE;
359 // do it
360 mediator->SetZLevel(this, aLevel);
361 PersistentAttributesDirty(PAD_MISC);
362 SavePersistentAttributes();
364 nsCOMPtr<nsIContentViewer> cv;
365 mDocShell->GetContentViewer(getter_AddRefs(cv));
366 if (cv) {
367 RefPtr<dom::Document> doc = cv->GetDocument();
368 if (doc) {
369 ErrorResult rv;
370 RefPtr<dom::Event> event =
371 doc->CreateEvent(u"Events"_ns, dom::CallerType::System, rv);
372 if (event) {
373 event->InitEvent(u"windowZLevel"_ns, true, false);
375 event->SetTrusted(true);
377 doc->DispatchEvent(*event);
381 return NS_OK;
384 NS_IMETHODIMP AppWindow::GetChromeFlags(uint32_t* aChromeFlags) {
385 NS_ENSURE_ARG_POINTER(aChromeFlags);
386 *aChromeFlags = mChromeFlags;
387 return NS_OK;
390 NS_IMETHODIMP AppWindow::SetChromeFlags(uint32_t aChromeFlags) {
391 NS_ASSERTION(!mChromeFlagsFrozen,
392 "SetChromeFlags() after AssumeChromeFlagsAreFrozen()!");
394 mChromeFlags = aChromeFlags;
395 if (mChromeLoaded) {
396 ApplyChromeFlags();
398 return NS_OK;
401 NS_IMETHODIMP AppWindow::AssumeChromeFlagsAreFrozen() {
402 mChromeFlagsFrozen = true;
403 return NS_OK;
406 NS_IMETHODIMP AppWindow::SetIntrinsicallySized(bool aIntrinsicallySized) {
407 mIntrinsicallySized = aIntrinsicallySized;
408 return NS_OK;
411 NS_IMETHODIMP AppWindow::GetIntrinsicallySized(bool* aIntrinsicallySized) {
412 NS_ENSURE_ARG_POINTER(aIntrinsicallySized);
414 *aIntrinsicallySized = mIntrinsicallySized;
415 return NS_OK;
418 NS_IMETHODIMP AppWindow::GetPrimaryContentShell(
419 nsIDocShellTreeItem** aDocShellTreeItem) {
420 NS_ENSURE_ARG_POINTER(aDocShellTreeItem);
421 NS_IF_ADDREF(*aDocShellTreeItem = mPrimaryContentShell);
422 return NS_OK;
425 NS_IMETHODIMP
426 AppWindow::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
427 if (aPrimary) {
428 mPrimaryBrowserParent = aTab;
429 mPrimaryContentShell = nullptr;
430 } else if (mPrimaryBrowserParent == aTab) {
431 mPrimaryBrowserParent = nullptr;
434 return NS_OK;
437 NS_IMETHODIMP
438 AppWindow::RemoteTabRemoved(nsIRemoteTab* aTab) {
439 if (aTab == mPrimaryBrowserParent) {
440 mPrimaryBrowserParent = nullptr;
443 return NS_OK;
446 NS_IMETHODIMP
447 AppWindow::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
448 nsCOMPtr<nsIRemoteTab> tab = mPrimaryBrowserParent;
449 tab.forget(aTab);
450 return NS_OK;
453 static LayoutDeviceIntSize GetOuterToInnerSizeDifference(nsIWidget* aWindow) {
454 if (!aWindow) {
455 return LayoutDeviceIntSize();
457 LayoutDeviceIntSize baseSize(200, 200);
458 LayoutDeviceIntSize windowSize = aWindow->ClientToWindowSize(baseSize);
459 return windowSize - baseSize;
462 static CSSIntSize GetOuterToInnerSizeDifferenceInCSSPixels(nsIWidget* aWindow) {
463 if (!aWindow) {
464 return {};
466 LayoutDeviceIntSize devPixelSize = GetOuterToInnerSizeDifference(aWindow);
467 return RoundedToInt(devPixelSize / aWindow->GetDefaultScale());
470 NS_IMETHODIMP
471 AppWindow::GetOuterToInnerHeightDifferenceInCSSPixels(uint32_t* aResult) {
472 *aResult = GetOuterToInnerSizeDifferenceInCSSPixels(mWindow).height;
473 return NS_OK;
476 NS_IMETHODIMP
477 AppWindow::GetOuterToInnerWidthDifferenceInCSSPixels(uint32_t* aResult) {
478 *aResult = GetOuterToInnerSizeDifferenceInCSSPixels(mWindow).width;
479 return NS_OK;
482 nsTArray<RefPtr<mozilla::LiveResizeListener>>
483 AppWindow::GetLiveResizeListeners() {
484 nsTArray<RefPtr<mozilla::LiveResizeListener>> listeners;
485 if (mPrimaryBrowserParent) {
486 BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
487 listeners.AppendElement(host->GetActor());
489 return listeners;
492 NS_IMETHODIMP AppWindow::AddChildWindow(nsIAppWindow* aChild) {
493 // we're not really keeping track of this right now
494 return NS_OK;
497 NS_IMETHODIMP AppWindow::RemoveChildWindow(nsIAppWindow* aChild) {
498 // we're not really keeping track of this right now
499 return NS_OK;
502 NS_IMETHODIMP AppWindow::ShowModal() {
503 AUTO_PROFILER_LABEL("AppWindow::ShowModal", OTHER);
505 // Store locally so it doesn't die on us
506 nsCOMPtr<nsIWidget> window = mWindow;
507 nsCOMPtr<nsIAppWindow> tempRef = this;
509 window->SetModal(true);
510 mContinueModalLoop = true;
511 EnableParent(false);
514 AutoNoJSAPI nojsapi;
515 SpinEventLoopUntil([&]() { return !mContinueModalLoop; });
518 mContinueModalLoop = false;
519 window->SetModal(false);
520 /* Note there's no EnableParent(true) here to match the false one
521 above. That's done in ExitModalLoop. It's important that the parent
522 be re-enabled before this window is made invisible; to do otherwise
523 causes bizarre z-ordering problems. At this point, the window is
524 already invisible.
525 No known current implementation of Enable would have a problem with
526 re-enabling the parent twice, so we could do it again here without
527 breaking any current implementation. But that's unnecessary if the
528 modal loop is always exited using ExitModalLoop (the other way would be
529 to change the protected member variable directly.)
532 return mModalStatus;
535 //*****************************************************************************
536 // AppWindow::nsIBaseWindow
537 //*****************************************************************************
539 NS_IMETHODIMP AppWindow::InitWindow(nativeWindow aParentNativeWindow,
540 nsIWidget* parentWidget, int32_t x,
541 int32_t y, int32_t cx, int32_t cy) {
542 // XXX First Check In
543 NS_ASSERTION(false, "Not Yet Implemented");
544 return NS_OK;
547 NS_IMETHODIMP AppWindow::Destroy() {
548 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
550 if (mDocShell) {
551 mDocShell->RemoveProgressListener(this);
555 MutexAutoLock lock(mSPTimerLock);
556 if (mSPTimer) {
557 mSPTimer->Cancel();
558 SavePersistentAttributes();
559 mSPTimer = nullptr;
563 if (!mWindow) return NS_OK;
565 // Ensure we don't reenter this code
566 if (mDestroying) return NS_OK;
568 mozilla::AutoRestore<bool> guard(mDestroying);
569 mDestroying = true;
571 nsCOMPtr<nsIAppShellService> appShell(
572 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
573 NS_ASSERTION(appShell, "Couldn't get appShell... xpcom shutdown?");
574 if (appShell)
575 appShell->UnregisterTopLevelWindow(static_cast<nsIAppWindow*>(this));
577 nsCOMPtr<nsIAppWindow> parentWindow(do_QueryReferent(mParentWindow));
578 if (parentWindow) parentWindow->RemoveChildWindow(this);
580 // Remove modality (if any) and hide while destroying. More than
581 // a convenience, the hide prevents user interaction with the partially
582 // destroyed window. This is especially necessary when the eldest window
583 // in a stack of modal windows is destroyed first. It happens.
584 ExitModalLoop(NS_OK);
585 // XXX: Skip unmapping the window on Linux due to GLX hangs on the compositor
586 // thread with NVIDIA driver 310.32. We don't need to worry about user
587 // interactions with destroyed windows on X11 either.
588 #ifndef MOZ_WIDGET_GTK
589 if (mWindow) mWindow->Show(false);
590 #endif
592 #if defined(XP_WIN)
593 // We need to explicitly set the focus on Windows, but
594 // only if the parent is visible.
595 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
596 if (parent) {
597 nsCOMPtr<nsIWidget> parentWidget;
598 parent->GetMainWidget(getter_AddRefs(parentWidget));
600 if (parentWidget && parentWidget->IsVisible()) {
601 bool isParentHiddenWindow = false;
603 if (appShell) {
604 bool hasHiddenWindow = false;
605 appShell->GetHasHiddenWindow(&hasHiddenWindow);
606 if (hasHiddenWindow) {
607 nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
608 nsCOMPtr<nsIAppWindow> hiddenWindow;
609 appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
610 if (hiddenWindow) {
611 baseHiddenWindow = do_GetInterface(hiddenWindow);
612 isParentHiddenWindow = (baseHiddenWindow == parent);
617 // somebody screwed up somewhere. hiddenwindow shouldn't be anybody's
618 // parent. still, when it happens, skip activating it.
619 if (!isParentHiddenWindow) {
620 parentWidget->PlaceBehind(eZPlacementTop, 0, true);
624 #endif
626 RemoveTooltipSupport();
628 mDOMWindow = nullptr;
629 if (mDocShell) {
630 RefPtr<BrowsingContext> bc(mDocShell->GetBrowsingContext());
631 mDocShell->Destroy();
632 bc->Detach();
633 mDocShell = nullptr; // this can cause reentrancy of this function
636 mPrimaryContentShell = nullptr;
638 if (mContentTreeOwner) {
639 mContentTreeOwner->AppWindow(nullptr);
640 NS_RELEASE(mContentTreeOwner);
642 if (mPrimaryContentTreeOwner) {
643 mPrimaryContentTreeOwner->AppWindow(nullptr);
644 NS_RELEASE(mPrimaryContentTreeOwner);
646 if (mChromeTreeOwner) {
647 mChromeTreeOwner->AppWindow(nullptr);
648 NS_RELEASE(mChromeTreeOwner);
650 if (mWindow) {
651 mWindow->SetWidgetListener(nullptr); // nsWebShellWindow hackery
652 mWindow->Destroy();
653 mWindow = nullptr;
656 if (!mIsHiddenWindow && mRegistered) {
657 /* Inform appstartup we've destroyed this window and it could
658 quit now if it wanted. This must happen at least after mDocShell
659 is destroyed, because onunload handlers fire then, and those being
660 script, anything could happen. A new window could open, even.
661 See bug 130719. */
662 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
663 NS_ASSERTION(obssvc, "Couldn't get observer service?");
665 if (obssvc)
666 obssvc->NotifyObservers(nullptr, "xul-window-destroyed", nullptr);
669 return NS_OK;
672 NS_IMETHODIMP AppWindow::GetDevicePixelsPerDesktopPixel(double* aScale) {
673 *aScale = mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0;
674 return NS_OK;
677 NS_IMETHODIMP AppWindow::GetUnscaledDevicePixelsPerCSSPixel(double* aScale) {
678 *aScale = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
679 return NS_OK;
682 NS_IMETHODIMP AppWindow::SetPositionDesktopPix(int32_t aX, int32_t aY) {
683 mWindow->Move(aX, aY);
684 if (mSizingShellFromXUL) {
685 // If we're invoked for sizing from XUL, we want to neither ignore anything
686 // nor persist anything, since it's already the value in XUL.
687 return NS_OK;
689 if (!mChromeLoaded) {
690 // If we're called before the chrome is loaded someone obviously wants this
691 // window at this position. We don't persist this one-time position.
692 mIgnoreXULPosition = true;
693 return NS_OK;
695 PersistentAttributesDirty(PAD_POSITION);
696 SavePersistentAttributes();
697 return NS_OK;
700 // The parameters here are device pixels; do the best we can to convert to
701 // desktop px, using the window's current scale factor (if available).
702 NS_IMETHODIMP AppWindow::SetPosition(int32_t aX, int32_t aY) {
703 // Don't reset the window's size mode here - platforms that don't want to move
704 // maximized windows should reset it in their respective Move implementation.
705 DesktopToLayoutDeviceScale currScale = mWindow->GetDesktopToDeviceScale();
706 DesktopPoint pos = LayoutDeviceIntPoint(aX, aY) / currScale;
707 return SetPositionDesktopPix(pos.x, pos.y);
710 NS_IMETHODIMP AppWindow::GetPosition(int32_t* aX, int32_t* aY) {
711 return GetPositionAndSize(aX, aY, nullptr, nullptr);
714 NS_IMETHODIMP AppWindow::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) {
715 /* any attempt to set the window's size or position overrides the window's
716 zoom state. this is important when these two states are competing while
717 the window is being opened. but it should probably just always be so. */
718 mWindow->SetSizeMode(nsSizeMode_Normal);
720 mIntrinsicallySized = false;
722 DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
723 DesktopSize size = LayoutDeviceIntSize(aCX, aCY) / scale;
724 mWindow->Resize(size.width, size.height, aRepaint);
725 if (mSizingShellFromXUL) {
726 // If we're invoked for sizing from XUL, we want to neither ignore anything
727 // nor persist anything, since it's already the value in XUL.
728 return NS_OK;
730 if (!mChromeLoaded) {
731 // If we're called before the chrome is loaded someone obviously wants this
732 // window at this size & in the normal size mode (since it is the only mode
733 // in which setting dimensions makes sense). We don't persist this one-time
734 // size.
735 mIgnoreXULSize = true;
736 mIgnoreXULSizeMode = true;
737 return NS_OK;
739 PersistentAttributesDirty(PAD_SIZE);
740 SavePersistentAttributes();
741 return NS_OK;
744 NS_IMETHODIMP AppWindow::GetSize(int32_t* aCX, int32_t* aCY) {
745 return GetPositionAndSize(nullptr, nullptr, aCX, aCY);
748 NS_IMETHODIMP AppWindow::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aCX,
749 int32_t aCY, uint32_t aFlags) {
750 /* any attempt to set the window's size or position overrides the window's
751 zoom state. this is important when these two states are competing while
752 the window is being opened. but it should probably just always be so. */
753 mWindow->SetSizeMode(nsSizeMode_Normal);
755 mIntrinsicallySized = false;
757 DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
758 DesktopRect rect = LayoutDeviceIntRect(aX, aY, aCX, aCY) / scale;
759 mWindow->Resize(rect.X(), rect.Y(), rect.Width(), rect.Height(),
760 !!(aFlags & nsIBaseWindow::eRepaint));
761 if (mSizingShellFromXUL) {
762 // If we're invoked for sizing from XUL, we want to neither ignore anything
763 // nor persist anything, since it's already the value in XUL.
764 return NS_OK;
766 if (!mChromeLoaded) {
767 // If we're called before the chrome is loaded someone obviously wants this
768 // window at this size and position. We don't persist this one-time setting.
769 mIgnoreXULPosition = true;
770 mIgnoreXULSize = true;
771 mIgnoreXULSizeMode = true;
772 return NS_OK;
774 PersistentAttributesDirty(PAD_POSITION | PAD_SIZE);
775 SavePersistentAttributes();
776 return NS_OK;
779 NS_IMETHODIMP AppWindow::GetPositionAndSize(int32_t* x, int32_t* y, int32_t* cx,
780 int32_t* cy) {
781 if (!mWindow) return NS_ERROR_FAILURE;
783 LayoutDeviceIntRect rect = mWindow->GetScreenBounds();
785 if (x) *x = rect.X();
786 if (y) *y = rect.Y();
787 if (cx) *cx = rect.Width();
788 if (cy) *cy = rect.Height();
790 return NS_OK;
793 NS_IMETHODIMP AppWindow::Center(nsIAppWindow* aRelative, bool aScreen,
794 bool aAlert) {
795 int32_t left, top, width, height, ourWidth, ourHeight;
796 bool screenCoordinates = false, windowCoordinates = false;
797 nsresult result;
799 if (!mChromeLoaded) {
800 // note we lose the parameters. at time of writing, this isn't a problem.
801 mCenterAfterLoad = true;
802 return NS_OK;
805 if (!aScreen && !aRelative) return NS_ERROR_INVALID_ARG;
807 nsCOMPtr<nsIScreenManager> screenmgr =
808 do_GetService("@mozilla.org/gfx/screenmanager;1", &result);
809 if (NS_FAILED(result)) return result;
811 nsCOMPtr<nsIScreen> screen;
813 if (aRelative) {
814 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aRelative, &result));
815 if (base) {
816 // get window rect
817 result = base->GetPositionAndSize(&left, &top, &width, &height);
818 if (NS_SUCCEEDED(result)) {
819 double scale;
820 if (NS_SUCCEEDED(base->GetDevicePixelsPerDesktopPixel(&scale))) {
821 left = NSToIntRound(left / scale);
822 top = NSToIntRound(top / scale);
823 width = NSToIntRound(width / scale);
824 height = NSToIntRound(height / scale);
826 // if centering on screen, convert that to the corresponding screen
827 if (aScreen)
828 screenmgr->ScreenForRect(left, top, width, height,
829 getter_AddRefs(screen));
830 else
831 windowCoordinates = true;
832 } else {
833 // something's wrong with the reference window.
834 // fall back to the primary screen
835 aRelative = 0;
836 aScreen = true;
840 if (!aRelative) {
841 if (!mOpenerScreenRect.IsEmpty()) {
842 // FIXME - check if these are device or display pixels
843 screenmgr->ScreenForRect(mOpenerScreenRect.X(), mOpenerScreenRect.Y(),
844 mOpenerScreenRect.Width(),
845 mOpenerScreenRect.Height(),
846 getter_AddRefs(screen));
847 } else {
848 screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
852 if (aScreen && screen) {
853 screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
854 screenCoordinates = true;
857 if (screenCoordinates || windowCoordinates) {
858 NS_ASSERTION(mWindow, "what, no window?");
859 double scale = mWindow->GetDesktopToDeviceScale().scale;
860 GetSize(&ourWidth, &ourHeight);
861 int32_t scaledWidth, scaledHeight;
862 scaledWidth = NSToIntRound(ourWidth / scale);
863 scaledHeight = NSToIntRound(ourHeight / scale);
864 left += (width - scaledWidth) / 2;
865 top += (height - scaledHeight) / (aAlert ? 3 : 2);
866 if (windowCoordinates) {
867 mWindow->ConstrainPosition(false, &left, &top);
869 SetPosition(left * scale, top * scale);
871 // If moving the window caused it to change size,
872 // re-do the centering.
873 int32_t newWidth, newHeight;
874 GetSize(&newWidth, &newHeight);
875 if (newWidth != ourWidth || newHeight != ourHeight) {
876 return Center(aRelative, aScreen, aAlert);
878 return NS_OK;
881 return NS_ERROR_FAILURE;
884 NS_IMETHODIMP AppWindow::Repaint(bool aForce) {
885 // XXX First Check In
886 NS_ASSERTION(false, "Not Yet Implemented");
887 return NS_OK;
890 NS_IMETHODIMP AppWindow::GetParentWidget(nsIWidget** aParentWidget) {
891 NS_ENSURE_ARG_POINTER(aParentWidget);
892 NS_ENSURE_STATE(mWindow);
894 NS_IF_ADDREF(*aParentWidget = mWindow->GetParent());
895 return NS_OK;
898 NS_IMETHODIMP AppWindow::SetParentWidget(nsIWidget* aParentWidget) {
899 // XXX First Check In
900 NS_ASSERTION(false, "Not Yet Implemented");
901 return NS_OK;
904 NS_IMETHODIMP AppWindow::GetParentNativeWindow(
905 nativeWindow* aParentNativeWindow) {
906 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
908 nsCOMPtr<nsIWidget> parentWidget;
909 NS_ENSURE_SUCCESS(GetParentWidget(getter_AddRefs(parentWidget)),
910 NS_ERROR_FAILURE);
912 if (parentWidget) {
913 *aParentNativeWindow = parentWidget->GetNativeData(NS_NATIVE_WIDGET);
916 return NS_OK;
919 NS_IMETHODIMP AppWindow::SetParentNativeWindow(
920 nativeWindow aParentNativeWindow) {
921 // XXX First Check In
922 NS_ASSERTION(false, "Not Yet Implemented");
923 return NS_OK;
926 NS_IMETHODIMP AppWindow::GetNativeHandle(nsAString& aNativeHandle) {
927 nsCOMPtr<nsIWidget> mainWidget;
928 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(mainWidget)),
929 NS_ERROR_FAILURE);
931 if (mainWidget) {
932 nativeWindow nativeWindowPtr = mainWidget->GetNativeData(NS_NATIVE_WINDOW);
933 /* the nativeWindow pointer is converted to and exposed as a string. This
934 is a more reliable way not to lose information (as opposed to JS
935 |Number| for instance) */
936 aNativeHandle =
937 NS_ConvertASCIItoUTF16(nsPrintfCString("0x%p", nativeWindowPtr));
940 return NS_OK;
943 NS_IMETHODIMP AppWindow::GetVisibility(bool* aVisibility) {
944 NS_ENSURE_ARG_POINTER(aVisibility);
946 // Always claim to be visible for now. See bug
947 // https://bugzilla.mozilla.org/show_bug.cgi?id=306245.
949 *aVisibility = true;
951 return NS_OK;
954 NS_IMETHODIMP AppWindow::SetVisibility(bool aVisibility) {
955 if (!mChromeLoaded) {
956 mShowAfterLoad = aVisibility;
957 return NS_OK;
960 if (mDebuting) {
961 return NS_OK;
964 NS_ENSURE_STATE(mDocShell);
966 mDebuting = true; // (Show / Focus is recursive)
968 // XXXTAB Do we really need to show docshell and the window? Isn't
969 // the window good enough?
970 mDocShell->SetVisibility(aVisibility);
971 // Store locally so it doesn't die on us. 'Show' can result in the window
972 // being closed with AppWindow::Destroy being called. That would set
973 // mWindow to null and posibly destroy the nsIWidget while its Show method
974 // is on the stack. We need to keep it alive until Show finishes.
975 nsCOMPtr<nsIWidget> window = mWindow;
976 window->Show(aVisibility);
978 nsCOMPtr<nsIWindowMediator> windowMediator(
979 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
980 if (windowMediator)
981 windowMediator->UpdateWindowTimeStamp(static_cast<nsIAppWindow*>(this));
983 // notify observers so that we can hide the splash screen if possible
984 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
985 NS_ASSERTION(obssvc, "Couldn't get observer service.");
986 if (obssvc) {
987 obssvc->NotifyObservers(static_cast<nsIAppWindow*>(this),
988 "xul-window-visible", nullptr);
991 mDebuting = false;
992 return NS_OK;
995 NS_IMETHODIMP AppWindow::GetEnabled(bool* aEnabled) {
996 NS_ENSURE_ARG_POINTER(aEnabled);
998 if (mWindow) {
999 *aEnabled = mWindow->IsEnabled();
1000 return NS_OK;
1003 *aEnabled = true; // better guess than most
1004 return NS_ERROR_FAILURE;
1007 NS_IMETHODIMP AppWindow::SetEnabled(bool aEnable) {
1008 if (mWindow) {
1009 mWindow->Enable(aEnable);
1010 return NS_OK;
1012 return NS_ERROR_FAILURE;
1015 NS_IMETHODIMP AppWindow::GetMainWidget(nsIWidget** aMainWidget) {
1016 NS_ENSURE_ARG_POINTER(aMainWidget);
1018 *aMainWidget = mWindow;
1019 NS_IF_ADDREF(*aMainWidget);
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 ErrorResult rv;
1109 *aAvailWidth = screen->GetAvailWidth(rv);
1110 if (NS_WARN_IF(rv.Failed())) {
1111 return rv.StealNSResult();
1114 *aAvailHeight = screen->GetAvailHeight(rv);
1115 if (NS_WARN_IF(rv.Failed())) {
1116 return rv.StealNSResult();
1119 return NS_OK;
1122 // Rounds window size to 1000x1000, or, if there isn't enough available
1123 // screen space, to a multiple of 200x100.
1124 NS_IMETHODIMP AppWindow::ForceRoundedDimensions() {
1125 if (mIsHiddenWindow) {
1126 return NS_OK;
1129 int32_t availWidthCSS = 0;
1130 int32_t availHeightCSS = 0;
1131 int32_t contentWidthCSS = 0;
1132 int32_t contentHeightCSS = 0;
1133 int32_t windowWidthCSS = 0;
1134 int32_t windowHeightCSS = 0;
1135 double devicePerCSSPixels = 1.0;
1137 GetUnscaledDevicePixelsPerCSSPixel(&devicePerCSSPixels);
1139 GetAvailScreenSize(&availWidthCSS, &availHeightCSS);
1141 // To get correct chrome size, we have to resize the window to a proper
1142 // size first. So, here, we size it to its available size.
1143 SetSpecifiedSize(availWidthCSS, availHeightCSS);
1145 // Get the current window size for calculating chrome UI size.
1146 GetSize(&windowWidthCSS, &windowHeightCSS); // device pixels
1147 windowWidthCSS = NSToIntRound(windowWidthCSS / devicePerCSSPixels);
1148 windowHeightCSS = NSToIntRound(windowHeightCSS / devicePerCSSPixels);
1150 // Get the content size for calculating chrome UI size.
1151 GetPrimaryContentSize(&contentWidthCSS, &contentHeightCSS);
1153 // Calculate the chrome UI size.
1154 int32_t chromeWidth = 0, chromeHeight = 0;
1155 chromeWidth = windowWidthCSS - contentWidthCSS;
1156 chromeHeight = windowHeightCSS - contentHeightCSS;
1158 int32_t targetContentWidth = 0, targetContentHeight = 0;
1160 // Here, we use the available screen dimensions as the input dimensions to
1161 // force the window to be rounded as the maximum available content size.
1162 nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
1163 chromeWidth, chromeHeight, availWidthCSS, availHeightCSS, availWidthCSS,
1164 availHeightCSS,
1165 false, // aSetOuterWidth
1166 false, // aSetOuterHeight
1167 &targetContentWidth, &targetContentHeight);
1169 targetContentWidth = NSToIntRound(targetContentWidth * devicePerCSSPixels);
1170 targetContentHeight = NSToIntRound(targetContentHeight * devicePerCSSPixels);
1172 SetPrimaryContentSize(targetContentWidth, targetContentHeight);
1174 return NS_OK;
1177 void AppWindow::OnChromeLoaded() {
1178 nsresult rv = EnsureContentTreeOwner();
1180 if (NS_SUCCEEDED(rv)) {
1181 mChromeLoaded = true;
1182 ApplyChromeFlags();
1183 SyncAttributesToWidget();
1184 if (mWindow) {
1185 SizeShell();
1186 if (mShowAfterLoad) {
1187 SetVisibility(true);
1189 AddTooltipSupport();
1191 // At this point the window may have been closed already during Show() or
1192 // SyncAttributesToWidget(), so AppWindow::Destroy may already have been
1193 // called. Take care!
1195 mPersistentAttributesMask |= PAD_POSITION | PAD_SIZE | PAD_MISC;
1198 bool AppWindow::NeedsTooltipListener() {
1199 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1200 if (!docShellElement || docShellElement->IsXULElement()) {
1201 // Tooltips in XUL are handled by each element.
1202 return false;
1204 // All other non-XUL document types need a tooltip listener.
1205 return true;
1208 void AppWindow::AddTooltipSupport() {
1209 if (!NeedsTooltipListener()) {
1210 return;
1212 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
1213 if (!listener) {
1214 return;
1217 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1218 MOZ_ASSERT(docShellElement);
1219 listener->AddTooltipSupport(docShellElement);
1222 void AppWindow::RemoveTooltipSupport() {
1223 if (!NeedsTooltipListener()) {
1224 return;
1226 nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
1227 if (!listener) {
1228 return;
1231 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1232 MOZ_ASSERT(docShellElement);
1233 listener->RemoveTooltipSupport(docShellElement);
1236 // If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes
1237 // to fit to the screen when staggering windows; if they're negative,
1238 // we use the window's current size instead.
1239 bool AppWindow::LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight) {
1240 bool gotPosition = false;
1242 // if we're the hidden window, don't try to validate our size/position. We're
1243 // special.
1244 if (mIsHiddenWindow) return false;
1246 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1247 NS_ENSURE_TRUE(windowElement, false);
1249 int32_t currX = 0;
1250 int32_t currY = 0;
1251 int32_t currWidth = 0;
1252 int32_t currHeight = 0;
1253 nsresult errorCode;
1254 int32_t temp;
1256 GetPositionAndSize(&currX, &currY, &currWidth, &currHeight);
1258 // Convert to global display pixels for consistent window management across
1259 // screens with diverse resolutions
1260 double devToDesktopScale = 1.0 / mWindow->GetDesktopToDeviceScale().scale;
1261 currX = NSToIntRound(currX * devToDesktopScale);
1262 currY = NSToIntRound(currY * devToDesktopScale);
1264 // For size, use specified value if > 0, else current value
1265 double devToCSSScale = 1.0 / mWindow->GetDefaultScale().scale;
1266 int32_t cssWidth =
1267 aSpecWidth > 0 ? aSpecWidth : NSToIntRound(currWidth * devToCSSScale);
1268 int32_t cssHeight =
1269 aSpecHeight > 0 ? aSpecHeight : NSToIntRound(currHeight * devToCSSScale);
1271 // Obtain the position information from the <xul:window> element.
1272 int32_t specX = currX;
1273 int32_t specY = currY;
1274 nsAutoString posString;
1276 windowElement->GetAttribute(SCREENX_ATTRIBUTE, posString);
1277 temp = posString.ToInteger(&errorCode);
1278 if (NS_SUCCEEDED(errorCode)) {
1279 specX = temp;
1280 gotPosition = true;
1282 windowElement->GetAttribute(SCREENY_ATTRIBUTE, posString);
1283 temp = posString.ToInteger(&errorCode);
1284 if (NS_SUCCEEDED(errorCode)) {
1285 specY = temp;
1286 gotPosition = true;
1289 if (gotPosition) {
1290 // our position will be relative to our parent, if any
1291 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
1292 if (parent) {
1293 int32_t parentX, parentY;
1294 if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
1295 double scale;
1296 if (NS_SUCCEEDED(parent->GetDevicePixelsPerDesktopPixel(&scale))) {
1297 parentX = NSToIntRound(parentX / scale);
1298 parentY = NSToIntRound(parentY / scale);
1300 specX += parentX;
1301 specY += parentY;
1303 } else {
1304 StaggerPosition(specX, specY, cssWidth, cssHeight);
1307 mWindow->ConstrainPosition(false, &specX, &specY);
1308 if (specX != currX || specY != currY) {
1309 SetPositionDesktopPix(specX, specY);
1312 return gotPosition;
1315 static Maybe<int32_t> ReadIntAttribute(const Element& aElement, nsAtom* aAtom) {
1316 nsAutoString attrString;
1317 if (!aElement.GetAttr(kNameSpaceID_None, aAtom, attrString)) {
1318 return Nothing();
1321 nsresult res = NS_OK;
1322 int32_t ret = attrString.ToInteger(&res);
1323 return NS_SUCCEEDED(res) ? Some(ret) : Nothing();
1326 static Maybe<int32_t> ReadSize(const Element& aElement, nsAtom* aAttr,
1327 nsAtom* aMinAttr, nsAtom* aMaxAttr) {
1328 Maybe<int32_t> attr = ReadIntAttribute(aElement, aAttr);
1329 if (!attr) {
1330 return Nothing();
1333 int32_t min =
1334 std::max(100, ReadIntAttribute(aElement, aMinAttr).valueOr(100));
1335 int32_t max = ReadIntAttribute(aElement, aMaxAttr)
1336 .valueOr(std::numeric_limits<int32_t>::max());
1338 return Some(std::min(max, std::max(*attr, min)));
1341 bool AppWindow::LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight) {
1342 bool gotSize = false;
1344 // if we're the hidden window, don't try to validate our size/position. We're
1345 // special.
1346 if (mIsHiddenWindow) {
1347 return false;
1350 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1351 NS_ENSURE_TRUE(windowElement, false);
1353 // Obtain the sizing information from the <xul:window> element.
1354 aSpecWidth = 100;
1355 aSpecHeight = 100;
1357 if (auto width = ReadSize(*windowElement, nsGkAtoms::width,
1358 nsGkAtoms::minwidth, nsGkAtoms::maxwidth)) {
1359 aSpecWidth = *width;
1360 gotSize = true;
1363 if (auto height = ReadSize(*windowElement, nsGkAtoms::height,
1364 nsGkAtoms::minheight, nsGkAtoms::maxheight)) {
1365 aSpecHeight = *height;
1366 gotSize = true;
1369 return gotSize;
1372 void AppWindow::SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight) {
1373 // constrain to screen size
1374 int32_t screenWidth;
1375 int32_t screenHeight;
1377 if (NS_SUCCEEDED(GetAvailScreenSize(&screenWidth, &screenHeight))) {
1378 if (aSpecWidth > screenWidth) {
1379 aSpecWidth = screenWidth;
1381 if (aSpecHeight > screenHeight) {
1382 aSpecHeight = screenHeight;
1386 NS_ASSERTION(mWindow, "we expected to have a window already");
1388 int32_t currWidth = 0;
1389 int32_t currHeight = 0;
1390 GetSize(&currWidth, &currHeight); // returns device pixels
1392 // convert specified values to device pixels, and resize if needed
1393 double cssToDevPx = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
1394 aSpecWidth = NSToIntRound(aSpecWidth * cssToDevPx);
1395 aSpecHeight = NSToIntRound(aSpecHeight * cssToDevPx);
1396 mIntrinsicallySized = false;
1397 if (aSpecWidth != currWidth || aSpecHeight != currHeight) {
1398 SetSize(aSpecWidth, aSpecHeight, false);
1402 /* Miscellaneous persistent attributes are attributes named in the
1403 |persist| attribute, other than size and position. Those are special
1404 because it's important to load those before one of the misc
1405 attributes (sizemode) and they require extra processing. */
1406 bool AppWindow::UpdateWindowStateFromMiscXULAttributes() {
1407 bool gotState = false;
1409 /* There are no misc attributes of interest to the hidden window.
1410 It's especially important not to try to validate that window's
1411 size or position, because some platforms (Mac OS X) need to
1412 make it visible and offscreen. */
1413 if (mIsHiddenWindow) return false;
1415 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1416 NS_ENSURE_TRUE(windowElement, false);
1418 nsAutoString stateString;
1419 nsSizeMode sizeMode = nsSizeMode_Normal;
1421 // If we are told to ignore the size mode attribute, force
1422 // normal sizemode.
1423 if (mIgnoreXULSizeMode) {
1424 windowElement->SetAttribute(MODE_ATTRIBUTE, SIZEMODE_NORMAL,
1425 IgnoreErrors());
1426 } else {
1427 // Otherwise, read sizemode from DOM and, if the window is resizable,
1428 // set it later.
1429 windowElement->GetAttribute(MODE_ATTRIBUTE, stateString);
1430 if ((stateString.Equals(SIZEMODE_MAXIMIZED) ||
1431 stateString.Equals(SIZEMODE_FULLSCREEN))) {
1432 /* Honor request to maximize only if the window is sizable.
1433 An unsizable, unmaximizable, yet maximized window confuses
1434 Windows OS and is something of a travesty, anyway. */
1435 if (mChromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
1436 mIntrinsicallySized = false;
1438 if (stateString.Equals(SIZEMODE_MAXIMIZED))
1439 sizeMode = nsSizeMode_Maximized;
1440 else
1441 sizeMode = nsSizeMode_Fullscreen;
1446 if (sizeMode == nsSizeMode_Fullscreen) {
1447 nsCOMPtr<mozIDOMWindowProxy> ourWindow;
1448 GetWindowDOMWindow(getter_AddRefs(ourWindow));
1449 auto* piWindow = nsPIDOMWindowOuter::From(ourWindow);
1450 piWindow->SetFullScreen(true);
1451 } else {
1452 // For maximized windows, ignore the XUL size and position attributes,
1453 // as setting them would set the window back to normal sizemode.
1454 if (sizeMode == nsSizeMode_Maximized) {
1455 mIgnoreXULSize = true;
1456 mIgnoreXULPosition = true;
1458 mWindow->SetSizeMode(sizeMode);
1460 gotState = true;
1462 // zlevel
1463 windowElement->GetAttribute(ZLEVEL_ATTRIBUTE, stateString);
1464 if (!stateString.IsEmpty()) {
1465 nsresult errorCode;
1466 int32_t zLevel = stateString.ToInteger(&errorCode);
1467 if (NS_SUCCEEDED(errorCode) && zLevel >= lowestZ && zLevel <= highestZ)
1468 SetZLevel(zLevel);
1471 return gotState;
1474 /* Stagger windows of the same type so they don't appear on top of each other.
1475 This code does have a scary double loop -- it'll keep passing through
1476 the entire list of open windows until it finds a non-collision. Doesn't
1477 seem to be a problem, but it deserves watching.
1478 The aRequested{X,Y} parameters here are in desktop pixels;
1479 the aSpec{Width,Height} parameters are CSS pixel dimensions.
1481 void AppWindow::StaggerPosition(int32_t& aRequestedX, int32_t& aRequestedY,
1482 int32_t aSpecWidth, int32_t aSpecHeight) {
1483 // These "constants" will be converted from CSS to desktop pixels
1484 // for the appropriate screen, assuming we find a screen to use...
1485 // hence they're not actually declared const here.
1486 int32_t kOffset = 22;
1487 uint32_t kSlop = 4;
1489 bool keepTrying;
1490 int bouncedX = 0, // bounced off vertical edge of screen
1491 bouncedY = 0; // bounced off horizontal edge
1493 // look for any other windows of this type
1494 nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
1495 if (!wm) return;
1497 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1498 if (!windowElement) return;
1500 nsCOMPtr<nsIAppWindow> ourAppWindow(this);
1502 nsAutoString windowType;
1503 windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, windowType);
1505 int32_t screenTop = 0, // it's pointless to initialize these ...
1506 screenRight = 0, // ... but to prevent oversalubrious and ...
1507 screenBottom = 0, // ... underbright compilers from ...
1508 screenLeft = 0; // ... issuing warnings.
1509 bool gotScreen = false;
1511 { // fetch screen coordinates
1512 nsCOMPtr<nsIScreenManager> screenMgr(
1513 do_GetService("@mozilla.org/gfx/screenmanager;1"));
1514 if (screenMgr) {
1515 nsCOMPtr<nsIScreen> ourScreen;
1516 // the coordinates here are already display pixels
1517 screenMgr->ScreenForRect(aRequestedX, aRequestedY, aSpecWidth,
1518 aSpecHeight, getter_AddRefs(ourScreen));
1519 if (ourScreen) {
1520 int32_t screenWidth, screenHeight;
1521 ourScreen->GetAvailRectDisplayPix(&screenLeft, &screenTop, &screenWidth,
1522 &screenHeight);
1523 screenBottom = screenTop + screenHeight;
1524 screenRight = screenLeft + screenWidth;
1525 // Get the screen's scaling factors and convert staggering constants
1526 // from CSS px to desktop pixel units
1527 double desktopToDeviceScale = 1.0, cssToDeviceScale = 1.0;
1528 ourScreen->GetContentsScaleFactor(&desktopToDeviceScale);
1529 ourScreen->GetDefaultCSSScaleFactor(&cssToDeviceScale);
1530 double cssToDesktopFactor = cssToDeviceScale / desktopToDeviceScale;
1531 kOffset = NSToIntRound(kOffset * cssToDesktopFactor);
1532 kSlop = NSToIntRound(kSlop * cssToDesktopFactor);
1533 // Convert dimensions from CSS to desktop pixels
1534 aSpecWidth = NSToIntRound(aSpecWidth * cssToDesktopFactor);
1535 aSpecHeight = NSToIntRound(aSpecHeight * cssToDesktopFactor);
1536 gotScreen = true;
1541 // One full pass through all windows of this type, repeat until no collisions.
1542 do {
1543 keepTrying = false;
1544 nsCOMPtr<nsISimpleEnumerator> windowList;
1545 wm->GetAppWindowEnumerator(windowType.get(), getter_AddRefs(windowList));
1547 if (!windowList) break;
1549 // One full pass through all windows of this type, offset and stop on
1550 // collision.
1551 do {
1552 bool more;
1553 windowList->HasMoreElements(&more);
1554 if (!more) break;
1556 nsCOMPtr<nsISupports> supportsWindow;
1557 windowList->GetNext(getter_AddRefs(supportsWindow));
1559 nsCOMPtr<nsIAppWindow> listAppWindow(do_QueryInterface(supportsWindow));
1560 if (listAppWindow != ourAppWindow) {
1561 int32_t listX, listY;
1562 nsCOMPtr<nsIBaseWindow> listBaseWindow(
1563 do_QueryInterface(supportsWindow));
1564 listBaseWindow->GetPosition(&listX, &listY);
1565 double scale;
1566 if (NS_SUCCEEDED(
1567 listBaseWindow->GetDevicePixelsPerDesktopPixel(&scale))) {
1568 listX = NSToIntRound(listX / scale);
1569 listY = NSToIntRound(listY / scale);
1572 if (Abs(listX - aRequestedX) <= kSlop &&
1573 Abs(listY - aRequestedY) <= kSlop) {
1574 // collision! offset and start over
1575 if (bouncedX & 0x1)
1576 aRequestedX -= kOffset;
1577 else
1578 aRequestedX += kOffset;
1579 aRequestedY += kOffset;
1581 if (gotScreen) {
1582 // if we're moving to the right and we need to bounce...
1583 if (!(bouncedX & 0x1) &&
1584 ((aRequestedX + aSpecWidth) > screenRight)) {
1585 aRequestedX = screenRight - aSpecWidth;
1586 ++bouncedX;
1589 // if we're moving to the left and we need to bounce...
1590 if ((bouncedX & 0x1) && aRequestedX < screenLeft) {
1591 aRequestedX = screenLeft;
1592 ++bouncedX;
1595 // if we hit the bottom then bounce to the top
1596 if (aRequestedY + aSpecHeight > screenBottom) {
1597 aRequestedY = screenTop;
1598 ++bouncedY;
1602 /* loop around again,
1603 but it's time to give up once we've covered the screen.
1604 there's a potential infinite loop with lots of windows. */
1605 keepTrying = bouncedX < 2 || bouncedY == 0;
1606 break;
1609 } while (true);
1610 } while (keepTrying);
1613 void AppWindow::SyncAttributesToWidget() {
1614 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1615 if (!windowElement) return;
1617 MOZ_DIAGNOSTIC_ASSERT(mWindow, "No widget on SyncAttributesToWidget?");
1619 nsAutoString attr;
1621 // "hidechrome" attribute
1622 if (windowElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidechrome,
1623 nsGkAtoms::_true, eCaseMatters)) {
1624 mWindow->HideWindowChrome(true);
1627 NS_ENSURE_TRUE_VOID(mWindow);
1629 // "chromemargin" attribute
1630 nsIntMargin margins;
1631 windowElement->GetAttribute(u"chromemargin"_ns, attr);
1632 if (nsContentUtils::ParseIntMarginValue(attr, margins)) {
1633 LayoutDeviceIntMargin tmp =
1634 LayoutDeviceIntMargin::FromUnknownMargin(margins);
1635 mWindow->SetNonClientMargins(tmp);
1638 NS_ENSURE_TRUE_VOID(mWindow);
1640 // "windowtype" attribute
1641 windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, attr);
1642 if (!attr.IsEmpty()) {
1643 mWindow->SetWindowClass(attr);
1646 NS_ENSURE_TRUE_VOID(mWindow);
1648 // "icon" attribute
1649 windowElement->GetAttribute(u"icon"_ns, attr);
1650 if (!attr.IsEmpty()) {
1651 mWindow->SetIcon(attr);
1653 NS_ENSURE_TRUE_VOID(mWindow);
1656 // "drawtitle" attribute
1657 windowElement->GetAttribute(u"drawtitle"_ns, attr);
1658 mWindow->SetDrawsTitle(attr.LowerCaseEqualsLiteral("true"));
1660 NS_ENSURE_TRUE_VOID(mWindow);
1662 // "toggletoolbar" attribute
1663 windowElement->GetAttribute(u"toggletoolbar"_ns, attr);
1664 mWindow->SetShowsToolbarButton(attr.LowerCaseEqualsLiteral("true"));
1666 NS_ENSURE_TRUE_VOID(mWindow);
1668 // "macnativefullscreen" attribute
1669 windowElement->GetAttribute(u"macnativefullscreen"_ns, attr);
1670 mWindow->SetSupportsNativeFullscreen(attr.LowerCaseEqualsLiteral("true"));
1672 NS_ENSURE_TRUE_VOID(mWindow);
1674 // "macanimationtype" attribute
1675 windowElement->GetAttribute(u"macanimationtype"_ns, attr);
1676 if (attr.EqualsLiteral("document")) {
1677 mWindow->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation);
1681 enum class ConversionDirection {
1682 InnerToOuter,
1683 OuterToInner,
1686 static void ConvertWindowSize(nsIAppWindow* aWin, const nsAtom* aAttr,
1687 ConversionDirection aDirection,
1688 nsAString& aInOutString) {
1689 MOZ_ASSERT(aWin);
1690 MOZ_ASSERT(aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height);
1692 nsresult rv;
1693 int32_t size = aInOutString.ToInteger(&rv);
1694 if (NS_FAILED(rv)) {
1695 return;
1698 int32_t sizeDiff = aAttr == nsGkAtoms::width
1699 ? aWin->GetOuterToInnerWidthDifferenceInCSSPixels()
1700 : aWin->GetOuterToInnerHeightDifferenceInCSSPixels();
1702 if (!sizeDiff) {
1703 return;
1706 int32_t multiplier = aDirection == ConversionDirection::InnerToOuter ? 1 : -1;
1708 CopyASCIItoUTF16(nsPrintfCString("%d", size + multiplier * sizeDiff),
1709 aInOutString);
1712 nsresult AppWindow::GetPersistentValue(const nsAtom* aAttr, nsAString& aValue) {
1713 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1714 if (!docShellElement) {
1715 return NS_ERROR_FAILURE;
1718 nsAutoString windowElementId;
1719 docShellElement->GetId(windowElementId);
1720 // Elements must have an ID to be persisted.
1721 if (windowElementId.IsEmpty()) {
1722 return NS_OK;
1725 RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
1726 nsIURI* docURI = ownerDoc->GetDocumentURI();
1727 if (!docURI) {
1728 return NS_ERROR_FAILURE;
1730 nsAutoCString utf8uri;
1731 nsresult rv = docURI->GetSpec(utf8uri);
1732 NS_ENSURE_SUCCESS(rv, rv);
1733 NS_ConvertUTF8toUTF16 uri(utf8uri);
1735 #ifdef MOZ_NEW_XULSTORE
1736 nsDependentAtomString attrString(aAttr);
1737 rv = XULStore::GetValue(uri, windowElementId, attrString, aValue);
1738 #else
1739 if (!mLocalStore) {
1740 mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1741 if (NS_WARN_IF(!mLocalStore)) {
1742 return NS_ERROR_NOT_INITIALIZED;
1746 rv = mLocalStore->GetValue(uri, windowElementId, nsDependentAtomString(aAttr),
1747 aValue);
1748 #endif
1749 if (NS_WARN_IF(NS_FAILED(rv))) {
1750 return rv;
1753 if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
1754 // Convert attributes from outer size to inner size for top-level
1755 // windows, see bug 1444525 & co.
1756 ConvertWindowSize(this, aAttr, ConversionDirection::OuterToInner, aValue);
1759 return NS_OK;
1762 nsresult AppWindow::GetDocXulStoreKeys(nsString& aUriSpec,
1763 nsString& aWindowElementId) {
1764 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1765 if (!docShellElement) {
1766 return NS_ERROR_FAILURE;
1769 docShellElement->GetId(aWindowElementId);
1770 // Match the behavior of XULPersist and only persist values if the element
1771 // has an ID.
1772 if (aWindowElementId.IsEmpty()) {
1773 return NS_OK;
1776 RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
1777 nsIURI* docURI = ownerDoc->GetDocumentURI();
1778 if (!docURI) {
1779 return NS_ERROR_FAILURE;
1782 nsAutoCString utf8uri;
1783 nsresult rv = docURI->GetSpec(utf8uri);
1784 if (NS_WARN_IF(NS_FAILED(rv))) {
1785 return rv;
1788 aUriSpec = NS_ConvertUTF8toUTF16(utf8uri);
1790 return NS_OK;
1793 nsresult AppWindow::MaybeSaveEarlyWindowPersistentValues(
1794 const LayoutDeviceIntRect& aRect) {
1795 #ifdef XP_WIN
1796 nsAutoString uri;
1797 nsAutoString windowElementId;
1798 nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
1800 if (NS_WARN_IF(NS_FAILED(rv))) {
1801 return rv;
1804 if (!windowElementId.EqualsLiteral("main-window") ||
1805 !uri.EqualsLiteral("chrome://browser/content/browser.xhtml")) {
1806 return NS_OK;
1809 SkeletonUISettings settings;
1811 settings.screenX = aRect.X();
1812 settings.screenY = aRect.Y();
1813 settings.width = aRect.Width();
1814 settings.height = aRect.Height();
1816 settings.maximized = mWindow->SizeMode() == nsSizeMode_Maximized;
1817 settings.cssToDevPixelScaling = mWindow->GetDefaultScale().scale;
1819 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
1820 Document* doc = windowElement->GetComposedDoc();
1821 Element* urlbarEl = doc->GetElementById(u"urlbar"_ns);
1823 nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow();
1824 nsCOMPtr<nsIDOMWindowUtils> utils =
1825 nsGlobalWindowOuter::Cast(window)->WindowUtils();
1826 RefPtr<dom::DOMRect> urlbarRect;
1827 rv = utils->GetBoundsWithoutFlushing(urlbarEl, getter_AddRefs(urlbarRect));
1828 if (NS_WARN_IF(NS_FAILED(rv))) {
1829 return rv;
1832 double urlbarX = urlbarRect->X();
1833 double urlbarWidth = urlbarRect->Width();
1835 // Hard-coding the following values and this behavior in general is rather
1836 // fragile, and can easily get out of sync with the actual front-end values.
1837 // This is not intended as a long-term solution, but only as the relatively
1838 // straightforward implementation of an experimental feature. If we want to
1839 // ship the skeleton UI to all users, we should strongly consider a more
1840 // robust solution than this. The vertical position of the urlbar will be
1841 // fixed.
1842 nsAutoString attributeValue;
1843 urlbarEl->GetAttribute(u"breakout-extend"_ns, attributeValue);
1844 // Scale down the urlbar if it is focused
1845 if (attributeValue.EqualsLiteral("true")) {
1846 // defined in browser.inc.css as 2px
1847 int urlbarBreakoutExtend = 2;
1848 // defined in urlbar-searchbar.inc.css as 5px
1849 int urlbarMarginInline = 5;
1851 // breakout-extend measurements are defined in urlbar-searchbar.inc.css
1852 urlbarX += (double)(urlbarBreakoutExtend + urlbarMarginInline);
1853 urlbarWidth -= (double)(2 * (urlbarBreakoutExtend + urlbarMarginInline));
1855 CSSPixelSpan urlbar;
1856 urlbar.start = urlbarX;
1857 urlbar.end = urlbar.start + urlbarWidth;
1858 settings.urlbarSpan = urlbar;
1860 Element* navbar = doc->GetElementById(u"nav-bar"_ns);
1862 Element* searchbarEl = doc->GetElementById(u"searchbar"_ns);
1863 CSSPixelSpan searchbar;
1864 if (navbar->Contains(searchbarEl)) {
1865 RefPtr<dom::DOMRect> searchbarRect;
1866 rv = utils->GetBoundsWithoutFlushing(searchbarEl,
1867 getter_AddRefs(searchbarRect));
1868 if (NS_WARN_IF(NS_FAILED(rv))) {
1869 return rv;
1871 searchbar.start = searchbarRect->X();
1872 searchbar.end = searchbar.start + searchbarRect->Width();
1873 } else {
1874 // There is no searchbar in the UI
1875 searchbar.start = 0;
1876 searchbar.end = 0;
1878 settings.searchbarSpan = searchbar;
1880 nsAutoString bookmarksVisibility;
1881 Preferences::GetString("browser.toolbars.bookmarks.visibility",
1882 bookmarksVisibility);
1883 settings.bookmarksToolbarShown =
1884 bookmarksVisibility.EqualsLiteral("always") ||
1885 (Preferences::GetBool("browser.toolbars.bookmarks.2h2020", false) &&
1886 bookmarksVisibility.EqualsLiteral("newtab"));
1888 Element* menubar = doc->GetElementById(u"toolbar-menubar"_ns);
1889 menubar->GetAttribute(u"autohide"_ns, attributeValue);
1890 settings.menubarShown = attributeValue.EqualsLiteral("false");
1892 ErrorResult err;
1893 nsCOMPtr<nsIHTMLCollection> toolbarSprings = navbar->GetElementsByTagNameNS(
1894 u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns,
1895 u"toolbarspring"_ns, err);
1896 if (err.Failed()) {
1897 return NS_ERROR_FAILURE;
1899 mozilla::Vector<CSSPixelSpan> springs;
1900 for (int i = 0; i < toolbarSprings->Length(); i++) {
1901 RefPtr<Element> springEl = toolbarSprings->Item(i);
1902 RefPtr<dom::DOMRect> springRect;
1903 rv = utils->GetBoundsWithoutFlushing(springEl, getter_AddRefs(springRect));
1904 if (NS_WARN_IF(NS_FAILED(rv))) {
1905 return rv;
1907 CSSPixelSpan spring;
1908 spring.start = springRect->X();
1909 spring.end = spring.start + springRect->Width();
1910 if (!settings.springs.append(spring)) {
1911 return NS_ERROR_FAILURE;
1915 settings.rtlEnabled = intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1917 bool isInTabletMode = false;
1918 bool autoTouchModePref =
1919 Preferences::GetBool("browser.touchmode.auto", false);
1920 if (autoTouchModePref) {
1921 nsCOMPtr<nsIWindowsUIUtils> uiUtils(
1922 do_GetService("@mozilla.org/windows-ui-utils;1"));
1923 if (!NS_WARN_IF(!uiUtils)) {
1924 uiUtils->GetInTabletMode(&isInTabletMode);
1928 if (isInTabletMode) {
1929 settings.uiDensity = SkeletonUIDensity::Touch;
1930 } else {
1931 int uiDensityPref = Preferences::GetInt("browser.uidensity", 0);
1932 switch (uiDensityPref) {
1933 case 0: {
1934 settings.uiDensity = SkeletonUIDensity::Default;
1935 break;
1937 case 1: {
1938 settings.uiDensity = SkeletonUIDensity::Compact;
1939 break;
1941 case 2: {
1942 settings.uiDensity = SkeletonUIDensity::Touch;
1943 break;
1948 Unused << PersistPreXULSkeletonUIValues(settings);
1949 #endif
1951 return NS_OK;
1954 nsresult AppWindow::SetPersistentValue(const nsAtom* aAttr,
1955 const nsAString& aValue) {
1956 nsAutoString uri;
1957 nsAutoString windowElementId;
1958 nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
1960 if (NS_FAILED(rv) || windowElementId.IsEmpty()) {
1961 return rv;
1964 nsAutoString maybeConvertedValue(aValue);
1965 if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
1966 // Make sure we store the <window> attributes as outer window size, see
1967 // bug 1444525 & co.
1968 ConvertWindowSize(this, aAttr, ConversionDirection::InnerToOuter,
1969 maybeConvertedValue);
1972 #ifdef MOZ_NEW_XULSTORE
1973 nsDependentAtomString attrString(aAttr);
1974 return XULStore::SetValue(uri, windowElementId, attrString,
1975 maybeConvertedValue);
1976 #else
1977 if (!mLocalStore) {
1978 mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1979 if (NS_WARN_IF(!mLocalStore)) {
1980 return NS_ERROR_NOT_INITIALIZED;
1984 return mLocalStore->SetValue(
1985 uri, windowElementId, nsDependentAtomString(aAttr), maybeConvertedValue);
1986 #endif
1989 NS_IMETHODIMP AppWindow::SavePersistentAttributes() {
1990 // can happen when the persistence timer fires at an inopportune time
1991 // during window shutdown
1992 if (!mDocShell) return NS_ERROR_FAILURE;
1994 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
1995 if (!docShellElement) return NS_ERROR_FAILURE;
1997 nsAutoString persistString;
1998 docShellElement->GetAttribute(PERSIST_ATTRIBUTE, persistString);
1999 if (persistString.IsEmpty()) { // quick check which sometimes helps
2000 mPersistentAttributesDirty = 0;
2001 return NS_OK;
2004 bool isFullscreen = false;
2005 if (nsPIDOMWindowOuter* domWindow = mDocShell->GetWindow()) {
2006 isFullscreen = domWindow->GetFullScreen();
2009 // get our size, position and mode to persist
2010 LayoutDeviceIntRect rect;
2011 bool gotRestoredBounds = NS_SUCCEEDED(mWindow->GetRestoredBounds(rect));
2013 // we use CSS pixels for size, but desktop pixels for position
2014 CSSToLayoutDeviceScale sizeScale = mWindow->GetDefaultScale();
2015 DesktopToLayoutDeviceScale posScale = mWindow->GetDesktopToDeviceScale();
2017 // make our position relative to our parent, if any
2018 nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
2019 if (parent && gotRestoredBounds) {
2020 int32_t parentX, parentY;
2021 if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) {
2022 rect.MoveBy(-parentX, -parentY);
2026 nsAutoString sizeString;
2027 bool shouldPersist = !isFullscreen;
2028 ErrorResult rv;
2029 // (only for size elements which are persisted)
2030 if ((mPersistentAttributesDirty & PAD_POSITION) && gotRestoredBounds) {
2031 if (persistString.Find("screenX") >= 0) {
2032 sizeString.Truncate();
2033 sizeString.AppendInt(NSToIntRound(rect.X() / posScale.scale));
2034 docShellElement->SetAttribute(SCREENX_ATTRIBUTE, sizeString, rv);
2035 if (shouldPersist) {
2036 Unused << SetPersistentValue(nsGkAtoms::screenX, sizeString);
2039 if (persistString.Find("screenY") >= 0) {
2040 sizeString.Truncate();
2041 sizeString.AppendInt(NSToIntRound(rect.Y() / posScale.scale));
2042 docShellElement->SetAttribute(SCREENY_ATTRIBUTE, sizeString, rv);
2043 if (shouldPersist) {
2044 Unused << SetPersistentValue(nsGkAtoms::screenY, sizeString);
2049 if ((mPersistentAttributesDirty & PAD_SIZE) && gotRestoredBounds) {
2050 LayoutDeviceIntRect innerRect =
2051 rect - GetOuterToInnerSizeDifference(mWindow);
2052 if (persistString.Find("width") >= 0) {
2053 sizeString.Truncate();
2054 sizeString.AppendInt(NSToIntRound(innerRect.Width() / sizeScale.scale));
2055 docShellElement->SetAttribute(WIDTH_ATTRIBUTE, sizeString, rv);
2056 if (shouldPersist) {
2057 Unused << SetPersistentValue(nsGkAtoms::width, sizeString);
2060 if (persistString.Find("height") >= 0) {
2061 sizeString.Truncate();
2062 sizeString.AppendInt(NSToIntRound(innerRect.Height() / sizeScale.scale));
2063 docShellElement->SetAttribute(HEIGHT_ATTRIBUTE, sizeString, rv);
2064 if (shouldPersist) {
2065 Unused << SetPersistentValue(nsGkAtoms::height, sizeString);
2070 Unused << MaybeSaveEarlyWindowPersistentValues(rect);
2072 if (mPersistentAttributesDirty & PAD_MISC) {
2073 nsSizeMode sizeMode = mWindow->SizeMode();
2075 if (sizeMode != nsSizeMode_Minimized) {
2076 if (sizeMode == nsSizeMode_Maximized)
2077 sizeString.Assign(SIZEMODE_MAXIMIZED);
2078 else if (sizeMode == nsSizeMode_Fullscreen)
2079 sizeString.Assign(SIZEMODE_FULLSCREEN);
2080 else
2081 sizeString.Assign(SIZEMODE_NORMAL);
2082 docShellElement->SetAttribute(MODE_ATTRIBUTE, sizeString, rv);
2083 if (shouldPersist && persistString.Find("sizemode") >= 0) {
2084 Unused << SetPersistentValue(nsGkAtoms::sizemode, sizeString);
2087 bool tiled = mWindow->IsTiled();
2088 if (tiled) {
2089 sizeString.Assign(u"true"_ns);
2090 } else {
2091 sizeString.Assign(u"false"_ns);
2093 docShellElement->SetAttribute(TILED_ATTRIBUTE, sizeString, rv);
2094 if (persistString.Find("zlevel") >= 0) {
2095 uint32_t zLevel;
2096 nsCOMPtr<nsIWindowMediator> mediator(
2097 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
2098 if (mediator) {
2099 mediator->GetZLevel(this, &zLevel);
2100 sizeString.Truncate();
2101 sizeString.AppendInt(zLevel);
2102 docShellElement->SetAttribute(ZLEVEL_ATTRIBUTE, sizeString, rv);
2103 if (shouldPersist) {
2104 Unused << SetPersistentValue(nsGkAtoms::zlevel, sizeString);
2110 mPersistentAttributesDirty = 0;
2111 return NS_OK;
2114 NS_IMETHODIMP AppWindow::GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow) {
2115 NS_ENSURE_STATE(mDocShell);
2117 if (!mDOMWindow) mDOMWindow = mDocShell->GetWindow();
2118 NS_ENSURE_TRUE(mDOMWindow, NS_ERROR_FAILURE);
2120 *aDOMWindow = mDOMWindow;
2121 NS_ADDREF(*aDOMWindow);
2122 return NS_OK;
2125 dom::Element* AppWindow::GetWindowDOMElement() const {
2126 NS_ENSURE_TRUE(mDocShell, nullptr);
2128 nsCOMPtr<nsIContentViewer> cv;
2129 mDocShell->GetContentViewer(getter_AddRefs(cv));
2130 NS_ENSURE_TRUE(cv, nullptr);
2132 const dom::Document* document = cv->GetDocument();
2133 NS_ENSURE_TRUE(document, nullptr);
2135 return document->GetRootElement();
2138 nsresult AppWindow::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
2139 bool aPrimary) {
2140 // Set the default content tree owner
2141 if (aPrimary) {
2142 NS_ENSURE_SUCCESS(EnsurePrimaryContentTreeOwner(), NS_ERROR_FAILURE);
2143 aContentShell->SetTreeOwner(mPrimaryContentTreeOwner);
2144 mPrimaryContentShell = aContentShell;
2145 mPrimaryBrowserParent = nullptr;
2146 } else {
2147 NS_ENSURE_SUCCESS(EnsureContentTreeOwner(), NS_ERROR_FAILURE);
2148 aContentShell->SetTreeOwner(mContentTreeOwner);
2149 if (mPrimaryContentShell == aContentShell) mPrimaryContentShell = nullptr;
2152 return NS_OK;
2155 nsresult AppWindow::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
2156 if (mPrimaryContentShell == aContentShell) {
2157 mPrimaryContentShell = nullptr;
2159 return NS_OK;
2162 NS_IMETHODIMP
2163 AppWindow::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
2164 if (mPrimaryBrowserParent) {
2165 return GetPrimaryRemoteTabSize(aWidth, aHeight);
2166 } else if (mPrimaryContentShell) {
2167 return GetPrimaryContentShellSize(aWidth, aHeight);
2169 return NS_ERROR_UNEXPECTED;
2172 nsresult AppWindow::GetPrimaryRemoteTabSize(int32_t* aWidth, int32_t* aHeight) {
2173 BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
2174 // Need strong ref, since Client* can run script.
2175 RefPtr<dom::Element> element = host->GetOwnerElement();
2176 NS_ENSURE_STATE(element);
2178 *aWidth = element->ClientWidth();
2179 *aHeight = element->ClientHeight();
2180 return NS_OK;
2183 nsresult AppWindow::GetPrimaryContentShellSize(int32_t* aWidth,
2184 int32_t* aHeight) {
2185 NS_ENSURE_STATE(mPrimaryContentShell);
2187 nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(mPrimaryContentShell));
2188 NS_ENSURE_STATE(shellWindow);
2190 int32_t devicePixelWidth, devicePixelHeight;
2191 double shellScale = 1.0;
2192 // We want to return CSS pixels. First, we get device pixels
2193 // from the content area...
2194 shellWindow->GetSize(&devicePixelWidth, &devicePixelHeight);
2195 // And then get the device pixel scaling factor. Dividing device
2196 // pixels by this scaling factor gives us CSS pixels.
2197 shellWindow->GetUnscaledDevicePixelsPerCSSPixel(&shellScale);
2198 *aWidth = NSToIntRound(devicePixelWidth / shellScale);
2199 *aHeight = NSToIntRound(devicePixelHeight / shellScale);
2200 return NS_OK;
2203 NS_IMETHODIMP
2204 AppWindow::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
2205 if (mPrimaryBrowserParent) {
2206 return SetPrimaryRemoteTabSize(aWidth, aHeight);
2207 } else if (mPrimaryContentShell) {
2208 return SizeShellTo(mPrimaryContentShell, aWidth, aHeight);
2210 return NS_ERROR_UNEXPECTED;
2213 nsresult AppWindow::SetPrimaryRemoteTabSize(int32_t aWidth, int32_t aHeight) {
2214 int32_t shellWidth, shellHeight;
2215 GetPrimaryRemoteTabSize(&shellWidth, &shellHeight);
2217 double scale = 1.0;
2218 GetUnscaledDevicePixelsPerCSSPixel(&scale);
2220 SizeShellToWithLimit(aWidth, aHeight, shellWidth * scale,
2221 shellHeight * scale);
2222 return NS_OK;
2225 nsresult AppWindow::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
2226 NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
2227 return mDocShell->GetSize(aWidth, aHeight);
2230 nsresult AppWindow::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
2231 return SizeShellTo(mDocShell, aWidth, aHeight);
2234 NS_IMETHODIMP AppWindow::SizeShellTo(nsIDocShellTreeItem* aShellItem,
2235 int32_t aCX, int32_t aCY) {
2236 // XXXTAB This is wrong, we should actually reflow based on the passed in
2237 // shell. For now we are hacking and doing delta sizing. This is bad
2238 // because it assumes all size we add will go to the shell which probably
2239 // won't happen.
2241 nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem));
2242 NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE);
2244 int32_t width = 0;
2245 int32_t height = 0;
2246 shellAsWin->GetSize(&width, &height);
2248 SizeShellToWithLimit(aCX, aCY, width, height);
2250 return NS_OK;
2253 NS_IMETHODIMP AppWindow::ExitModalLoop(nsresult aStatus) {
2254 if (mContinueModalLoop) EnableParent(true);
2255 mContinueModalLoop = false;
2256 mModalStatus = aStatus;
2257 return NS_OK;
2260 // top-level function to create a new window
2261 NS_IMETHODIMP AppWindow::CreateNewWindow(int32_t aChromeFlags,
2262 nsIOpenWindowInfo* aOpenWindowInfo,
2263 nsIAppWindow** _retval) {
2264 NS_ENSURE_ARG_POINTER(_retval);
2266 if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME) {
2267 MOZ_RELEASE_ASSERT(
2268 !aOpenWindowInfo,
2269 "Unexpected nsOpenWindowInfo when creating a new chrome window");
2270 return CreateNewChromeWindow(aChromeFlags, _retval);
2273 return CreateNewContentWindow(aChromeFlags, aOpenWindowInfo, _retval);
2276 NS_IMETHODIMP AppWindow::CreateNewChromeWindow(int32_t aChromeFlags,
2277 nsIAppWindow** _retval) {
2278 nsCOMPtr<nsIAppShellService> appShell(
2279 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
2280 NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
2282 // Just do a normal create of a window and return.
2283 nsCOMPtr<nsIAppWindow> newWindow;
2284 appShell->CreateTopLevelWindow(
2285 this, nullptr, aChromeFlags, nsIAppShellService::SIZE_TO_CONTENT,
2286 nsIAppShellService::SIZE_TO_CONTENT, getter_AddRefs(newWindow));
2288 NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
2290 newWindow.forget(_retval);
2292 return NS_OK;
2295 NS_IMETHODIMP AppWindow::CreateNewContentWindow(
2296 int32_t aChromeFlags, nsIOpenWindowInfo* aOpenWindowInfo,
2297 nsIAppWindow** _retval) {
2298 nsCOMPtr<nsIAppShellService> appShell(
2299 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
2300 NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
2302 // We need to create a new top level window and then enter a nested
2303 // loop. Eventually the new window will be told that it has loaded,
2304 // at which time we know it is safe to spin out of the nested loop
2305 // and allow the opening code to proceed.
2307 nsCOMPtr<nsIURI> uri;
2308 nsAutoCString urlStr;
2309 urlStr.AssignLiteral(BROWSER_CHROME_URL_QUOTED);
2311 nsCOMPtr<nsIIOService> service(do_GetService(NS_IOSERVICE_CONTRACTID));
2312 if (service) {
2313 service->NewURI(urlStr, nullptr, nullptr, getter_AddRefs(uri));
2315 NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
2317 // We need to create a chrome window to contain the content window we're about
2318 // to pass back. The subject principal needs to be system while we're creating
2319 // it to make things work right, so force a system caller. See bug 799348
2320 // comment 13 for a description of what happens when we don't.
2321 nsCOMPtr<nsIAppWindow> newWindow;
2323 AutoNoJSAPI nojsapi;
2324 appShell->CreateTopLevelWindow(this, uri, aChromeFlags, 615, 480,
2325 getter_AddRefs(newWindow));
2326 NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
2329 AppWindow* appWin =
2330 static_cast<AppWindow*>(static_cast<nsIAppWindow*>(newWindow));
2332 // Specify which flags should be used by browser.xhtml to create the initial
2333 // content browser window.
2334 appWin->mInitialOpenWindowInfo = aOpenWindowInfo;
2336 // Specify that we want the window to remain locked until the chrome has
2337 // loaded.
2338 appWin->LockUntilChromeLoad();
2341 AutoNoJSAPI nojsapi;
2342 SpinEventLoopUntil([&]() { return !appWin->IsLocked(); });
2345 NS_ENSURE_STATE(appWin->mPrimaryContentShell ||
2346 appWin->mPrimaryBrowserParent);
2347 MOZ_ASSERT_IF(appWin->mPrimaryContentShell,
2348 !aOpenWindowInfo->GetNextRemoteBrowser());
2350 newWindow.forget(_retval);
2352 return NS_OK;
2355 NS_IMETHODIMP AppWindow::GetHasPrimaryContent(bool* aResult) {
2356 *aResult = mPrimaryBrowserParent || mPrimaryContentShell;
2357 return NS_OK;
2360 void AppWindow::EnableParent(bool aEnable) {
2361 nsCOMPtr<nsIBaseWindow> parentWindow;
2362 nsCOMPtr<nsIWidget> parentWidget;
2364 parentWindow = do_QueryReferent(mParentWindow);
2365 if (parentWindow) parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
2366 if (parentWidget) parentWidget->Enable(aEnable);
2369 // Constrain the window to its proper z-level
2370 bool AppWindow::ConstrainToZLevel(bool aImmediate, nsWindowZ* aPlacement,
2371 nsIWidget* aReqBelow,
2372 nsIWidget** aActualBelow) {
2373 #if 0
2374 /* Do we have a parent window? This means our z-order is already constrained,
2375 since we're a dependent window. Our window list isn't hierarchical,
2376 so we can't properly calculate placement for such a window.
2377 Should we just abort? */
2378 nsCOMPtr<nsIBaseWindow> parentWindow = do_QueryReferent(mParentWindow);
2379 if (parentWindow)
2380 return false;
2381 #endif
2383 nsCOMPtr<nsIWindowMediator> mediator(
2384 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
2385 if (!mediator) return false;
2387 bool altered;
2388 uint32_t position, newPosition, zLevel;
2389 nsIAppWindow* us = this;
2391 altered = false;
2392 mediator->GetZLevel(this, &zLevel);
2394 // translate from WidgetGUIEvent to nsIWindowMediator constants
2395 position = nsIWindowMediator::zLevelTop;
2396 if (*aPlacement == nsWindowZBottom || zLevel == nsIAppWindow::lowestZ)
2397 position = nsIWindowMediator::zLevelBottom;
2398 else if (*aPlacement == nsWindowZRelative)
2399 position = nsIWindowMediator::zLevelBelow;
2401 if (NS_SUCCEEDED(mediator->CalculateZPosition(
2402 us, position, aReqBelow, &newPosition, aActualBelow, &altered))) {
2403 /* If we were asked to move to the top but constrained to remain
2404 below one of our other windows, first move all windows in that
2405 window's layer and above to the top. This allows the user to
2406 click a window which can't be topmost and still bring mozilla
2407 to the foreground. */
2408 if (altered &&
2409 (position == nsIWindowMediator::zLevelTop ||
2410 (position == nsIWindowMediator::zLevelBelow && aReqBelow == 0)))
2411 PlaceWindowLayersBehind(zLevel + 1, nsIAppWindow::highestZ, 0);
2413 if (*aPlacement != nsWindowZBottom &&
2414 position == nsIWindowMediator::zLevelBottom)
2415 altered = true;
2416 if (altered || aImmediate) {
2417 if (newPosition == nsIWindowMediator::zLevelTop)
2418 *aPlacement = nsWindowZTop;
2419 else if (newPosition == nsIWindowMediator::zLevelBottom)
2420 *aPlacement = nsWindowZBottom;
2421 else
2422 *aPlacement = nsWindowZRelative;
2424 if (aImmediate) {
2425 nsCOMPtr<nsIBaseWindow> ourBase = do_QueryObject(this);
2426 if (ourBase) {
2427 nsCOMPtr<nsIWidget> ourWidget;
2428 ourBase->GetMainWidget(getter_AddRefs(ourWidget));
2429 ourWidget->PlaceBehind(*aPlacement == nsWindowZBottom
2430 ? eZPlacementBottom
2431 : eZPlacementBelow,
2432 *aActualBelow, false);
2437 /* CalculateZPosition can tell us to be below nothing, because it tries
2438 not to change something it doesn't recognize. A request to verify
2439 being below an unrecognized window, then, is treated as a request
2440 to come to the top (below null) */
2441 nsCOMPtr<nsIAppWindow> windowAbove;
2442 if (newPosition == nsIWindowMediator::zLevelBelow && *aActualBelow) {
2443 windowAbove = (*aActualBelow)->GetWidgetListener()->GetAppWindow();
2446 mediator->SetZPosition(us, newPosition, windowAbove);
2449 return altered;
2452 /* Re-z-position all windows in the layers from aLowLevel to aHighLevel,
2453 inclusive, to be behind aBehind. aBehind of null means on top.
2454 Note this method actually does nothing to our relative window positions.
2455 (And therefore there's no need to inform WindowMediator we're moving
2456 things, because we aren't.) This method is useful for, say, moving
2457 a range of layers of our own windows relative to windows belonging to
2458 external applications.
2460 void AppWindow::PlaceWindowLayersBehind(uint32_t aLowLevel, uint32_t aHighLevel,
2461 nsIAppWindow* aBehind) {
2462 // step through windows in z-order from top to bottommost window
2464 nsCOMPtr<nsIWindowMediator> mediator(
2465 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
2466 if (!mediator) return;
2468 nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
2469 mediator->GetZOrderAppWindowEnumerator(0, true,
2470 getter_AddRefs(windowEnumerator));
2471 if (!windowEnumerator) return;
2473 // each window will be moved behind previousHighWidget, itself
2474 // a moving target. initialize it.
2475 nsCOMPtr<nsIWidget> previousHighWidget;
2476 if (aBehind) {
2477 nsCOMPtr<nsIBaseWindow> highBase(do_QueryInterface(aBehind));
2478 if (highBase) highBase->GetMainWidget(getter_AddRefs(previousHighWidget));
2481 // get next lower window
2482 bool more;
2483 while (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)) && more) {
2484 uint32_t nextZ; // z-level of nextWindow
2485 nsCOMPtr<nsISupports> nextWindow;
2486 windowEnumerator->GetNext(getter_AddRefs(nextWindow));
2487 nsCOMPtr<nsIAppWindow> nextAppWindow(do_QueryInterface(nextWindow));
2488 nextAppWindow->GetZLevel(&nextZ);
2489 if (nextZ < aLowLevel)
2490 break; // we've processed all windows through aLowLevel
2492 // move it just below its next higher window
2493 nsCOMPtr<nsIBaseWindow> nextBase(do_QueryInterface(nextAppWindow));
2494 if (nextBase) {
2495 nsCOMPtr<nsIWidget> nextWidget;
2496 nextBase->GetMainWidget(getter_AddRefs(nextWidget));
2497 if (nextZ <= aHighLevel)
2498 nextWidget->PlaceBehind(eZPlacementBelow, previousHighWidget, false);
2499 previousHighWidget = nextWidget;
2504 void AppWindow::SetContentScrollbarVisibility(bool aVisible) {
2505 nsCOMPtr<nsPIDOMWindowOuter> contentWin(
2506 do_GetInterface(mPrimaryContentShell));
2507 if (!contentWin) {
2508 return;
2511 nsContentUtils::SetScrollbarsVisibility(contentWin->GetDocShell(), aVisible);
2514 // during spinup, attributes that haven't been loaded yet can't be dirty
2515 void AppWindow::PersistentAttributesDirty(uint32_t aDirtyFlags) {
2516 mPersistentAttributesDirty |= aDirtyFlags & mPersistentAttributesMask;
2519 void AppWindow::ApplyChromeFlags() {
2520 nsCOMPtr<dom::Element> window = GetWindowDOMElement();
2521 if (!window) {
2522 return;
2525 if (mChromeLoaded) {
2526 // The two calls in this block don't need to happen early because they
2527 // don't cause a global restyle on the document. Not only that, but the
2528 // scrollbar stuff needs a content area to toggle the scrollbars on anyway.
2529 // So just don't do these until mChromeLoaded is true.
2531 // Scrollbars have their own special treatment.
2532 SetContentScrollbarVisibility(mChromeFlags &
2533 nsIWebBrowserChrome::CHROME_SCROLLBARS);
2536 /* the other flags are handled together. we have style rules
2537 in navigator.css that trigger visibility based on
2538 the 'chromehidden' attribute of the <window> tag. */
2539 nsAutoString newvalue;
2541 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_MENUBAR))
2542 newvalue.AppendLiteral("menubar ");
2544 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_TOOLBAR))
2545 newvalue.AppendLiteral("toolbar ");
2547 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_LOCATIONBAR))
2548 newvalue.AppendLiteral("location ");
2550 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR))
2551 newvalue.AppendLiteral("directories ");
2553 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_STATUSBAR))
2554 newvalue.AppendLiteral("status ");
2556 if (!(mChromeFlags & nsIWebBrowserChrome::CHROME_EXTRA))
2557 newvalue.AppendLiteral("extrachrome ");
2559 // Note that if we're not actually changing the value this will be a no-op,
2560 // so no need to compare to the old value.
2561 IgnoredErrorResult rv;
2562 window->SetAttribute(u"chromehidden"_ns, newvalue, rv);
2565 NS_IMETHODIMP
2566 AppWindow::BeforeStartLayout() {
2567 ApplyChromeFlags();
2568 LoadPersistentWindowState();
2569 SyncAttributesToWidget();
2570 if (mWindow) {
2571 SizeShell();
2573 return NS_OK;
2576 NS_IMETHODIMP
2577 AppWindow::LockAspectRatio(bool aShouldLock) {
2578 mWindow->LockAspectRatio(aShouldLock);
2579 return NS_OK;
2582 void AppWindow::LoadPersistentWindowState() {
2583 nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
2584 if (!docShellElement) {
2585 return;
2588 // Check if the window wants to persist anything.
2589 nsAutoString persist;
2590 docShellElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
2591 if (persist.IsEmpty()) {
2592 return;
2595 auto loadValue = [&](const nsAtom* aAttr) {
2596 nsDependentAtomString attrString(aAttr);
2597 if (persist.Find(attrString) >= 0) {
2598 nsAutoString value;
2599 nsresult rv = GetPersistentValue(aAttr, value);
2600 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get persistent state.");
2601 if (NS_SUCCEEDED(rv) && !value.IsEmpty()) {
2602 IgnoredErrorResult err;
2603 docShellElement->SetAttribute(attrString, value, err);
2608 loadValue(nsGkAtoms::screenX);
2609 loadValue(nsGkAtoms::screenY);
2610 loadValue(nsGkAtoms::width);
2611 loadValue(nsGkAtoms::height);
2612 loadValue(nsGkAtoms::sizemode);
2615 void AppWindow::SizeShell() {
2616 AutoRestore<bool> sizingShellFromXUL(mSizingShellFromXUL);
2617 mSizingShellFromXUL = true;
2619 int32_t specWidth = -1, specHeight = -1;
2620 bool gotSize = false;
2622 nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
2623 nsAutoString windowType;
2624 if (windowElement) {
2625 windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, windowType);
2628 CSSIntSize windowDiff = GetOuterToInnerSizeDifferenceInCSSPixels(mWindow);
2630 // If we're using fingerprint resistance, we're going to resize the window
2631 // once we have primary content.
2632 if (nsContentUtils::ShouldResistFingerprinting() &&
2633 windowType.EqualsLiteral("navigator:browser")) {
2634 // Once we've got primary content, force dimensions.
2635 if (mPrimaryContentShell || mPrimaryBrowserParent) {
2636 ForceRoundedDimensions();
2638 // Always avoid setting size/sizemode on this window.
2639 mIgnoreXULSize = true;
2640 mIgnoreXULSizeMode = true;
2641 } else if (!mIgnoreXULSize) {
2642 gotSize = LoadSizeFromXUL(specWidth, specHeight);
2643 specWidth += windowDiff.width;
2644 specHeight += windowDiff.height;
2647 bool positionSet = !mIgnoreXULPosition;
2648 nsCOMPtr<nsIAppWindow> parentWindow(do_QueryReferent(mParentWindow));
2649 #if defined(XP_UNIX) && !defined(XP_MACOSX)
2650 // don't override WM placement on unix for independent, top-level windows
2651 // (however, we think the benefits of intelligent dependent window placement
2652 // trump that override.)
2653 if (!parentWindow) positionSet = false;
2654 #endif
2655 if (positionSet) {
2656 // We have to do this before sizing the window, because sizing depends
2657 // on the resolution of the screen we're on. But positioning needs to
2658 // know the size so that it can constrain to screen bounds.... as an
2659 // initial guess here, we'll use the specified size (if any).
2660 positionSet = LoadPositionFromXUL(specWidth, specHeight);
2663 if (gotSize) {
2664 SetSpecifiedSize(specWidth, specHeight);
2667 if (mIntrinsicallySized) {
2668 // (if LoadSizeFromXUL set the size, mIntrinsicallySized will be false)
2669 nsCOMPtr<nsIContentViewer> cv;
2670 mDocShell->GetContentViewer(getter_AddRefs(cv));
2671 if (cv) {
2672 RefPtr<nsDocShell> docShell = mDocShell;
2673 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
2674 docShell->GetTreeOwner(getter_AddRefs(treeOwner));
2675 if (treeOwner) {
2676 // GetContentSize can fail, so initialise |width| and |height| to be
2677 // on the safe side.
2678 int32_t width = 0, height = 0;
2679 if (NS_SUCCEEDED(cv->GetContentSize(&width, &height))) {
2680 treeOwner->SizeShellTo(docShell, width, height);
2681 // Update specified size for the final LoadPositionFromXUL call.
2682 specWidth = width + windowDiff.width;
2683 specHeight = height + windowDiff.height;
2689 // Now that we have set the window's final size, we can re-do its
2690 // positioning so that it is properly constrained to the screen.
2691 if (positionSet) {
2692 LoadPositionFromXUL(specWidth, specHeight);
2695 UpdateWindowStateFromMiscXULAttributes();
2697 if (mChromeLoaded && mCenterAfterLoad && !positionSet &&
2698 mWindow->SizeMode() == nsSizeMode_Normal) {
2699 Center(parentWindow, parentWindow ? false : true, false);
2703 NS_IMETHODIMP AppWindow::GetXULBrowserWindow(
2704 nsIXULBrowserWindow** aXULBrowserWindow) {
2705 NS_IF_ADDREF(*aXULBrowserWindow = mXULBrowserWindow);
2706 return NS_OK;
2709 NS_IMETHODIMP AppWindow::SetXULBrowserWindow(
2710 nsIXULBrowserWindow* aXULBrowserWindow) {
2711 mXULBrowserWindow = aXULBrowserWindow;
2712 return NS_OK;
2715 void AppWindow::SizeShellToWithLimit(int32_t aDesiredWidth,
2716 int32_t aDesiredHeight,
2717 int32_t shellItemWidth,
2718 int32_t shellItemHeight) {
2719 int32_t widthDelta = aDesiredWidth - shellItemWidth;
2720 int32_t heightDelta = aDesiredHeight - shellItemHeight;
2722 if (widthDelta || heightDelta) {
2723 int32_t winWidth = 0;
2724 int32_t winHeight = 0;
2726 GetSize(&winWidth, &winHeight);
2727 // There's no point in trying to make the window smaller than the
2728 // desired content area size --- that's not likely to work. This whole
2729 // function assumes that the outer docshell is adding some constant
2730 // "border" chrome to the content area.
2731 winWidth = std::max(winWidth + widthDelta, aDesiredWidth);
2732 winHeight = std::max(winHeight + heightDelta, aDesiredHeight);
2733 SetSize(winWidth, winHeight, true);
2737 nsresult AppWindow::GetTabCount(uint32_t* aResult) {
2738 if (mXULBrowserWindow) {
2739 return mXULBrowserWindow->GetTabCount(aResult);
2742 *aResult = 0;
2743 return NS_OK;
2746 nsresult AppWindow::GetInitialOpenWindowInfo(
2747 nsIOpenWindowInfo** aOpenWindowInfo) {
2748 NS_ENSURE_ARG_POINTER(aOpenWindowInfo);
2749 *aOpenWindowInfo = do_AddRef(mInitialOpenWindowInfo).take();
2750 return NS_OK;
2753 PresShell* AppWindow::GetPresShell() {
2754 if (!mDocShell) {
2755 return nullptr;
2757 return mDocShell->GetPresShell();
2760 bool AppWindow::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) {
2761 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
2762 if (pm) {
2763 nsCOMPtr<nsPIDOMWindowOuter> window =
2764 mDocShell ? mDocShell->GetWindow() : nullptr;
2765 pm->AdjustPopupsOnWindowChange(window);
2768 // Notify all tabs that the widget moved.
2769 if (mDocShell && mDocShell->GetWindow()) {
2770 nsCOMPtr<EventTarget> eventTarget =
2771 mDocShell->GetWindow()->GetTopWindowRoot();
2772 nsContentUtils::DispatchChromeEvent(
2773 mDocShell->GetDocument(), eventTarget, u"MozUpdateWindowPos"_ns,
2774 CanBubble::eNo, Cancelable::eNo, nullptr);
2777 // Persist position, but not immediately, in case this OS is firing
2778 // repeated move events as the user drags the window
2779 SetPersistenceTimer(PAD_POSITION);
2780 return false;
2783 bool AppWindow::WindowResized(nsIWidget* aWidget, int32_t aWidth,
2784 int32_t aHeight) {
2785 if (mDocShell) {
2786 mDocShell->SetPositionAndSize(0, 0, aWidth, aHeight, 0);
2788 // Persist size, but not immediately, in case this OS is firing
2789 // repeated size events as the user drags the sizing handle
2790 if (!IsLocked()) SetPersistenceTimer(PAD_POSITION | PAD_SIZE | PAD_MISC);
2791 // Check if we need to continue a fullscreen change.
2792 switch (mFullscreenChangeState) {
2793 case FullscreenChangeState::WillChange:
2794 mFullscreenChangeState = FullscreenChangeState::WidgetResized;
2795 break;
2796 case FullscreenChangeState::WidgetEnteredFullscreen:
2797 FinishFullscreenChange(true);
2798 break;
2799 case FullscreenChangeState::WidgetExitedFullscreen:
2800 FinishFullscreenChange(false);
2801 break;
2802 case FullscreenChangeState::WidgetResized:
2803 case FullscreenChangeState::NotChanging:
2804 break;
2806 return true;
2809 bool AppWindow::RequestWindowClose(nsIWidget* aWidget) {
2810 // Maintain a reference to this as it is about to get destroyed.
2811 nsCOMPtr<nsIAppWindow> appWindow(this);
2813 nsCOMPtr<nsPIDOMWindowOuter> window(mDocShell ? mDocShell->GetWindow()
2814 : nullptr);
2815 nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window);
2817 RefPtr<PresShell> presShell = mDocShell->GetPresShell();
2818 if (!presShell) {
2819 mozilla::DebugOnly<bool> dying;
2820 MOZ_ASSERT(NS_SUCCEEDED(mDocShell->IsBeingDestroyed(&dying)) && dying,
2821 "No presShell, but window is not being destroyed");
2822 } else if (eventTarget) {
2823 RefPtr<nsPresContext> presContext = presShell->GetPresContext();
2825 nsEventStatus status = nsEventStatus_eIgnore;
2826 WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
2827 if (NS_SUCCEEDED(EventDispatcher::Dispatch(eventTarget, presContext, &event,
2828 nullptr, &status)) &&
2829 status == nsEventStatus_eConsumeNoDefault)
2830 return false;
2833 Destroy();
2834 return false;
2837 void AppWindow::SizeModeChanged(nsSizeMode sizeMode) {
2838 // An alwaysRaised (or higher) window will hide any newly opened normal
2839 // browser windows, so here we just drop a raised window to the normal
2840 // zlevel if it's maximized. We make no provision for automatically
2841 // re-raising it when restored.
2842 if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) {
2843 uint32_t zLevel;
2844 GetZLevel(&zLevel);
2845 if (zLevel > nsIAppWindow::normalZ) SetZLevel(nsIAppWindow::normalZ);
2847 mWindow->SetSizeMode(sizeMode);
2849 // Persist mode, but not immediately, because in many (all?)
2850 // cases this will merge with the similar call in NS_SIZE and
2851 // write the attribute values only once.
2852 SetPersistenceTimer(PAD_MISC);
2853 nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
2854 mDocShell ? mDocShell->GetWindow() : nullptr;
2855 if (ourWindow) {
2856 // Ensure that the fullscreen state is synchronized between
2857 // the widget and the outer window object.
2858 if (sizeMode == nsSizeMode_Fullscreen) {
2859 ourWindow->SetFullScreen(true);
2860 } else if (sizeMode != nsSizeMode_Minimized) {
2861 if (ourWindow->GetFullScreen()) {
2862 // The first SetFullscreenInternal call below ensures that we do
2863 // not trigger any fullscreen transition even if the window was
2864 // put in fullscreen only for the Fullscreen API. The second
2865 // SetFullScreen call ensures that the window really exit from
2866 // fullscreen even if it entered fullscreen for both Fullscreen
2867 // Mode and Fullscreen API.
2868 ourWindow->SetFullscreenInternal(
2869 FullscreenReason::ForForceExitFullscreen, false);
2870 ourWindow->SetFullScreen(false);
2874 // And always fire a user-defined sizemodechange event on the window
2875 ourWindow->DispatchCustomEvent(u"sizemodechange"_ns);
2878 if (PresShell* presShell = GetPresShell()) {
2879 presShell->GetPresContext()->SizeModeChanged(sizeMode);
2882 // Note the current implementation of SetSizeMode just stores
2883 // the new state; it doesn't actually resize. So here we store
2884 // the state and pass the event on to the OS. The day is coming
2885 // when we'll handle the event here, and the return result will
2886 // then need to be different.
2889 void AppWindow::UIResolutionChanged() {
2890 nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
2891 mDocShell ? mDocShell->GetWindow() : nullptr;
2892 if (ourWindow) {
2893 ourWindow->DispatchCustomEvent(u"resolutionchange"_ns,
2894 ChromeOnlyDispatch::eYes);
2898 void AppWindow::FullscreenWillChange(bool aInFullscreen) {
2899 if (mDocShell) {
2900 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2901 ourWindow->FullscreenWillChange(aInFullscreen);
2904 MOZ_ASSERT(mFullscreenChangeState == FullscreenChangeState::NotChanging);
2905 mFullscreenChangeState = FullscreenChangeState::WillChange;
2908 void AppWindow::FullscreenChanged(bool aInFullscreen) {
2909 if (mFullscreenChangeState == FullscreenChangeState::WidgetResized) {
2910 FinishFullscreenChange(aInFullscreen);
2911 } else {
2912 NS_WARNING_ASSERTION(
2913 mFullscreenChangeState == FullscreenChangeState::WillChange,
2914 "Unexpected fullscreen change state");
2915 FullscreenChangeState newState =
2916 aInFullscreen ? FullscreenChangeState::WidgetEnteredFullscreen
2917 : FullscreenChangeState::WidgetExitedFullscreen;
2918 mFullscreenChangeState = newState;
2919 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
2920 // Wait for resize for a small amount of time.
2921 // 80ms is actually picked arbitrarily. But it shouldn't be too large
2922 // in case the widget resize is not going to happen at all, which can
2923 // be the case for some Linux window managers and possibly Android.
2924 NS_DelayedDispatchToCurrentThread(
2925 NS_NewRunnableFunction(
2926 "AppWindow::FullscreenChanged",
2927 [this, kungFuDeathGrip, newState, aInFullscreen]() {
2928 if (mFullscreenChangeState == newState) {
2929 FinishFullscreenChange(aInFullscreen);
2932 80);
2936 void AppWindow::FinishFullscreenChange(bool aInFullscreen) {
2937 mFullscreenChangeState = FullscreenChangeState::NotChanging;
2938 if (mDocShell) {
2939 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2940 ourWindow->FinishFullscreenChange(aInFullscreen);
2945 void AppWindow::MacFullscreenMenubarOverlapChanged(
2946 mozilla::DesktopCoord aOverlapAmount) {
2947 if (mDocShell) {
2948 if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
2949 ourWindow->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
2954 void AppWindow::OcclusionStateChanged(bool aIsFullyOccluded) {
2955 nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
2956 mDocShell ? mDocShell->GetWindow() : nullptr;
2957 if (ourWindow) {
2958 // And always fire a user-defined occlusionstatechange event on the window
2959 ourWindow->DispatchCustomEvent(u"occlusionstatechange"_ns,
2960 ChromeOnlyDispatch::eYes);
2964 void AppWindow::OSToolbarButtonPressed() {
2965 // Keep a reference as setting the chrome flags can fire events.
2966 nsCOMPtr<nsIAppWindow> appWindow(this);
2968 // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA"
2969 // due to components with multiple sidebar components
2970 // (such as Mail/News, Addressbook, etc)... and frankly,
2971 // Mac IE, OmniWeb, and other Mac OS X apps all work this way
2972 uint32_t chromeMask = (nsIWebBrowserChrome::CHROME_TOOLBAR |
2973 nsIWebBrowserChrome::CHROME_LOCATIONBAR |
2974 nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
2976 nsCOMPtr<nsIWebBrowserChrome> wbc(do_GetInterface(appWindow));
2977 if (!wbc) return;
2979 uint32_t chromeFlags, newChromeFlags = 0;
2980 wbc->GetChromeFlags(&chromeFlags);
2981 newChromeFlags = chromeFlags & chromeMask;
2982 if (!newChromeFlags)
2983 chromeFlags |= chromeMask;
2984 else
2985 chromeFlags &= (~newChromeFlags);
2986 wbc->SetChromeFlags(chromeFlags);
2989 bool AppWindow::ZLevelChanged(bool aImmediate, nsWindowZ* aPlacement,
2990 nsIWidget* aRequestBelow,
2991 nsIWidget** aActualBelow) {
2992 if (aActualBelow) *aActualBelow = nullptr;
2994 return ConstrainToZLevel(aImmediate, aPlacement, aRequestBelow, aActualBelow);
2997 void AppWindow::WindowActivated() {
2998 nsCOMPtr<nsIAppWindow> appWindow(this);
3000 // focusing the window could cause it to close, so keep a reference to it
3001 nsCOMPtr<nsPIDOMWindowOuter> window =
3002 mDocShell ? mDocShell->GetWindow() : nullptr;
3003 nsFocusManager* fm = nsFocusManager::GetFocusManager();
3004 if (fm && window) {
3005 fm->WindowRaised(window, nsFocusManager::GenerateFocusActionId());
3008 if (mChromeLoaded) {
3009 PersistentAttributesDirty(PAD_POSITION | PAD_SIZE | PAD_MISC);
3010 SavePersistentAttributes();
3014 void AppWindow::WindowDeactivated() {
3015 nsCOMPtr<nsIAppWindow> appWindow(this);
3017 nsCOMPtr<nsPIDOMWindowOuter> window =
3018 mDocShell ? mDocShell->GetWindow() : nullptr;
3019 nsFocusManager* fm = nsFocusManager::GetFocusManager();
3020 if (fm && window && !fm->IsTestMode()) {
3021 fm->WindowLowered(window, nsFocusManager::GenerateFocusActionId());
3025 #ifdef USE_NATIVE_MENUS
3026 static void LoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
3027 if (gfxPlatform::IsHeadless()) {
3028 return;
3031 // Find the menubar tag (if there is more than one, we ignore all but
3032 // the first).
3033 nsCOMPtr<nsINodeList> menubarElements = aDoc->GetElementsByTagNameNS(
3034 nsLiteralString(
3035 u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
3036 u"menubar"_ns);
3038 nsCOMPtr<nsINode> menubarNode;
3039 if (menubarElements) {
3040 menubarNode = menubarElements->Item(0);
3043 using widget::NativeMenuSupport;
3044 if (menubarNode) {
3045 nsCOMPtr<Element> menubarContent(do_QueryInterface(menubarNode));
3046 NativeMenuSupport::CreateNativeMenuBar(aParentWindow, menubarContent);
3047 } else {
3048 NativeMenuSupport::CreateNativeMenuBar(aParentWindow, nullptr);
3052 class L10nReadyPromiseHandler final : public dom::PromiseNativeHandler {
3053 public:
3054 NS_DECL_ISUPPORTS
3056 L10nReadyPromiseHandler(Document* aDoc, nsIWidget* aParentWindow)
3057 : mDocument(aDoc), mWindow(aParentWindow) {}
3059 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
3060 LoadNativeMenus(mDocument, mWindow);
3063 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
3064 // Again, this shouldn't happen, but fallback to loading the menus as is.
3065 NS_WARNING(
3066 "L10nReadyPromiseHandler rejected - loading fallback native "
3067 "menu.");
3068 LoadNativeMenus(mDocument, mWindow);
3071 private:
3072 ~L10nReadyPromiseHandler() = default;
3074 RefPtr<Document> mDocument;
3075 nsCOMPtr<nsIWidget> mWindow;
3078 NS_IMPL_ISUPPORTS0(L10nReadyPromiseHandler)
3080 #endif
3082 class AppWindowTimerCallback final : public nsITimerCallback, public nsINamed {
3083 public:
3084 explicit AppWindowTimerCallback(AppWindow* aWindow) : mWindow(aWindow) {}
3086 NS_DECL_THREADSAFE_ISUPPORTS
3088 NS_IMETHOD Notify(nsITimer* aTimer) override {
3089 // Although this object participates in a refcount cycle (this -> mWindow
3090 // -> mSPTimer -> this), mSPTimer is a one-shot timer and releases this
3091 // after it fires. So we don't need to release mWindow here.
3093 mWindow->FirePersistenceTimer();
3094 return NS_OK;
3097 NS_IMETHOD GetName(nsACString& aName) override {
3098 aName.AssignLiteral("AppWindowTimerCallback");
3099 return NS_OK;
3102 private:
3103 ~AppWindowTimerCallback() {}
3105 RefPtr<AppWindow> mWindow;
3108 NS_IMPL_ISUPPORTS(AppWindowTimerCallback, nsITimerCallback, nsINamed)
3110 void AppWindow::SetPersistenceTimer(uint32_t aDirtyFlags) {
3111 MutexAutoLock lock(mSPTimerLock);
3112 if (!mSPTimer) {
3113 mSPTimer = NS_NewTimer();
3114 if (!mSPTimer) {
3115 NS_WARNING("Couldn't create @mozilla.org/timer;1 instance?");
3116 return;
3120 RefPtr<AppWindowTimerCallback> callback = new AppWindowTimerCallback(this);
3121 mSPTimer->InitWithCallback(callback, SIZE_PERSISTENCE_TIMEOUT,
3122 nsITimer::TYPE_ONE_SHOT);
3124 PersistentAttributesDirty(aDirtyFlags);
3127 void AppWindow::FirePersistenceTimer() {
3128 MutexAutoLock lock(mSPTimerLock);
3129 SavePersistentAttributes();
3132 //----------------------------------------
3133 // nsIWebProgessListener implementation
3134 //----------------------------------------
3135 NS_IMETHODIMP
3136 AppWindow::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3137 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
3138 int32_t aCurTotalProgress,
3139 int32_t aMaxTotalProgress) {
3140 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3141 return NS_OK;
3144 NS_IMETHODIMP
3145 AppWindow::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3146 uint32_t aStateFlags, nsresult aStatus) {
3147 // If the notification is not about a document finishing, then just
3148 // ignore it...
3149 if (!(aStateFlags & nsIWebProgressListener::STATE_STOP) ||
3150 !(aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) {
3151 return NS_OK;
3154 if (mChromeLoaded) return NS_OK;
3156 // If this document notification is for a frame then ignore it...
3157 nsCOMPtr<mozIDOMWindowProxy> eventWin;
3158 aProgress->GetDOMWindow(getter_AddRefs(eventWin));
3159 auto* eventPWin = nsPIDOMWindowOuter::From(eventWin);
3160 if (eventPWin) {
3161 nsPIDOMWindowOuter* rootPWin = eventPWin->GetPrivateRoot();
3162 if (eventPWin != rootPWin) return NS_OK;
3165 mChromeLoaded = true;
3166 mLockedUntilChromeLoad = false;
3168 #ifdef USE_NATIVE_MENUS
3169 ///////////////////////////////
3170 // Find the Menubar DOM and Load the menus, hooking them up to the loaded
3171 // commands
3172 ///////////////////////////////
3173 nsCOMPtr<nsIContentViewer> cv;
3174 mDocShell->GetContentViewer(getter_AddRefs(cv));
3175 if (cv) {
3176 RefPtr<Document> menubarDoc = cv->GetDocument();
3177 if (menubarDoc) {
3178 RefPtr<DocumentL10n> l10n = menubarDoc->GetL10n();
3179 if (l10n) {
3180 // Wait for l10n to be ready so the menus are localized.
3181 RefPtr<Promise> promise = l10n->Ready();
3182 MOZ_ASSERT(promise);
3183 RefPtr<L10nReadyPromiseHandler> handler =
3184 new L10nReadyPromiseHandler(menubarDoc, mWindow);
3185 promise->AppendNativeHandler(handler);
3186 } else {
3187 // Something went wrong loading the doc and l10n wasn't created. This
3188 // shouldn't really happen, but if it does fallback to trying to load
3189 // the menus as is.
3190 LoadNativeMenus(menubarDoc, mWindow);
3194 #endif // USE_NATIVE_MENUS
3196 OnChromeLoaded();
3198 return NS_OK;
3201 NS_IMETHODIMP
3202 AppWindow::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
3203 nsIURI* aURI, uint32_t aFlags) {
3204 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3205 return NS_OK;
3208 NS_IMETHODIMP
3209 AppWindow::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3210 nsresult aStatus, const char16_t* aMessage) {
3211 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3212 return NS_OK;
3215 NS_IMETHODIMP
3216 AppWindow::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3217 uint32_t aState) {
3218 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3219 return NS_OK;
3222 NS_IMETHODIMP
3223 AppWindow::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
3224 nsIRequest* aRequest, uint32_t aEvent) {
3225 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
3226 return NS_OK;
3230 * ExecuteCloseHandler - Run the close handler, if any.
3231 * @return true iff we found a close handler to run.
3233 bool AppWindow::ExecuteCloseHandler() {
3234 /* If the event handler closes this window -- a likely scenario --
3235 things get deleted out of order without this death grip.
3236 (The problem may be the death grip in nsWindow::windowProc,
3237 which forces this window's widget to remain alive longer
3238 than it otherwise would.) */
3239 nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
3241 nsCOMPtr<EventTarget> eventTarget;
3242 if (mDocShell) {
3243 eventTarget = do_QueryInterface(mDocShell->GetWindow());
3246 if (eventTarget) {
3247 nsCOMPtr<nsIContentViewer> contentViewer;
3248 mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
3249 if (contentViewer) {
3250 RefPtr<nsPresContext> presContext = contentViewer->GetPresContext();
3252 nsEventStatus status = nsEventStatus_eIgnore;
3253 WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
3255 nsresult rv = EventDispatcher::Dispatch(eventTarget, presContext, &event,
3256 nullptr, &status);
3257 if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault)
3258 return true;
3259 // else fall through and return false
3263 return false;
3264 } // ExecuteCloseHandler
3266 void AppWindow::ConstrainToOpenerScreen(int32_t* aX, int32_t* aY) {
3267 if (mOpenerScreenRect.IsEmpty()) {
3268 *aX = *aY = 0;
3269 return;
3272 int32_t left, top, width, height;
3273 // Constrain initial positions to the same screen as opener
3274 nsCOMPtr<nsIScreenManager> screenmgr =
3275 do_GetService("@mozilla.org/gfx/screenmanager;1");
3276 if (screenmgr) {
3277 nsCOMPtr<nsIScreen> screen;
3278 screenmgr->ScreenForRect(
3279 mOpenerScreenRect.X(), mOpenerScreenRect.Y(), mOpenerScreenRect.Width(),
3280 mOpenerScreenRect.Height(), getter_AddRefs(screen));
3281 if (screen) {
3282 screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
3283 if (*aX < left || *aX > left + width) {
3284 *aX = left;
3286 if (*aY < top || *aY > top + height) {
3287 *aY = top;
3293 nsIAppWindow* AppWindow::WidgetListenerDelegate::GetAppWindow() {
3294 return mAppWindow->GetAppWindow();
3297 PresShell* AppWindow::WidgetListenerDelegate::GetPresShell() {
3298 return mAppWindow->GetPresShell();
3301 bool AppWindow::WidgetListenerDelegate::WindowMoved(nsIWidget* aWidget,
3302 int32_t aX, int32_t aY) {
3303 RefPtr<AppWindow> holder = mAppWindow;
3304 return holder->WindowMoved(aWidget, aX, aY);
3307 bool AppWindow::WidgetListenerDelegate::WindowResized(nsIWidget* aWidget,
3308 int32_t aWidth,
3309 int32_t aHeight) {
3310 RefPtr<AppWindow> holder = mAppWindow;
3311 return holder->WindowResized(aWidget, aWidth, aHeight);
3314 bool AppWindow::WidgetListenerDelegate::RequestWindowClose(nsIWidget* aWidget) {
3315 RefPtr<AppWindow> holder = mAppWindow;
3316 return holder->RequestWindowClose(aWidget);
3319 void AppWindow::WidgetListenerDelegate::SizeModeChanged(nsSizeMode aSizeMode) {
3320 RefPtr<AppWindow> holder = mAppWindow;
3321 holder->SizeModeChanged(aSizeMode);
3324 void AppWindow::WidgetListenerDelegate::UIResolutionChanged() {
3325 RefPtr<AppWindow> holder = mAppWindow;
3326 holder->UIResolutionChanged();
3329 void AppWindow::WidgetListenerDelegate::FullscreenWillChange(
3330 bool aInFullscreen) {
3331 RefPtr<AppWindow> holder = mAppWindow;
3332 holder->FullscreenWillChange(aInFullscreen);
3335 void AppWindow::WidgetListenerDelegate::FullscreenChanged(bool aInFullscreen) {
3336 RefPtr<AppWindow> holder = mAppWindow;
3337 holder->FullscreenChanged(aInFullscreen);
3340 void AppWindow::WidgetListenerDelegate::MacFullscreenMenubarOverlapChanged(
3341 DesktopCoord aOverlapAmount) {
3342 RefPtr<AppWindow> holder = mAppWindow;
3343 return holder->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
3346 void AppWindow::WidgetListenerDelegate::OcclusionStateChanged(
3347 bool aIsFullyOccluded) {
3348 RefPtr<AppWindow> holder = mAppWindow;
3349 holder->OcclusionStateChanged(aIsFullyOccluded);
3352 void AppWindow::WidgetListenerDelegate::OSToolbarButtonPressed() {
3353 RefPtr<AppWindow> holder = mAppWindow;
3354 holder->OSToolbarButtonPressed();
3357 bool AppWindow::WidgetListenerDelegate::ZLevelChanged(
3358 bool aImmediate, nsWindowZ* aPlacement, nsIWidget* aRequestBelow,
3359 nsIWidget** aActualBelow) {
3360 RefPtr<AppWindow> holder = mAppWindow;
3361 return holder->ZLevelChanged(aImmediate, aPlacement, aRequestBelow,
3362 aActualBelow);
3365 void AppWindow::WidgetListenerDelegate::WindowActivated() {
3366 RefPtr<AppWindow> holder = mAppWindow;
3367 holder->WindowActivated();
3370 void AppWindow::WidgetListenerDelegate::WindowDeactivated() {
3371 RefPtr<AppWindow> holder = mAppWindow;
3372 holder->WindowDeactivated();
3375 } // namespace mozilla