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