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