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