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