1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsIAppShellService.h"
7 #include "nsIComponentManager.h"
8 #include "nsIURL.h"
9 #include "nsNetUtil.h"
10 #include "nsIServiceManager.h"
11 #include "nsIObserverService.h"
12 #include "nsIObserver.h"
13 #include "nsIXPConnect.h"
14 #include "nsIXULRuntime.h"
15
16 #include "nsIWindowMediator.h"
17 #include "nsIWindowWatcher.h"
18 #include "nsPIWindowWatcher.h"
19 #include "nsIDOMWindow.h"
20 #include "nsPIDOMWindow.h"
21 #include "nsWebShellWindow.h"
22
23 #include "nsWidgetInitData.h"
24 #include "nsWidgetsCID.h"
25 #include "nsIWidget.h"
26 #include "nsIRequestObserver.h"
27 #include "nsIEmbeddingSiteWindow.h"
28
29 #include "nsAppShellService.h"
30 #include "nsContentUtils.h"
31 #include "nsThreadUtils.h"
32 #include "nsISupportsPrimitives.h"
33 #include "nsILoadContext.h"
34 #include "nsIWebNavigation.h"
35 #include "nsIWindowlessBrowser.h"
36
37 #include "mozilla/Attributes.h"
38 #include "mozilla/Preferences.h"
39 #include "mozilla/Services.h"
40 #include "mozilla/StartupTimeline.h"
41 #include "mozilla/intl/LocaleService.h"
42
43 #include "nsEmbedCID.h"
44 #include "nsIWebBrowser.h"
45 #include "nsIDocShell.h"
46 #include "gfxPlatform.h"
47
48 #ifdef MOZ_INSTRUMENT_EVENT_LOOP
49 #include "EventTracer.h"
50 #endif
51
52 using namespace mozilla;
53 using mozilla::intl::LocaleService;
54
55 // Default URL for the hidden window, can be overridden by a pref on Mac
56 #define DEFAULT_HIDDENWINDOW_URL "resource://gre-resources/hiddenWindow.html"
57
58 class nsIAppShell;
59
nsAppShellService()60 nsAppShellService::nsAppShellService()
61 : mXPCOMWillShutDown(false),
62 mXPCOMShuttingDown(false),
63 mModalWindowCount(0),
64 mApplicationProvidedHiddenWindow(false),
65 mScreenId(0) {
66 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
67
68 if (obs) {
69 obs->AddObserver(this, "xpcom-will-shutdown", false);
70 obs->AddObserver(this, "xpcom-shutdown", false);
71 }
72 }
73
~nsAppShellService()74 nsAppShellService::~nsAppShellService() {}
75
76 /*
77 * Implement the nsISupports methods...
78 */
NS_IMPL_ISUPPORTS(nsAppShellService,nsIAppShellService,nsIObserver)79 NS_IMPL_ISUPPORTS(nsAppShellService, nsIAppShellService, nsIObserver)
80
81 NS_IMETHODIMP
82 nsAppShellService::CreateHiddenWindow() {
83 return CreateHiddenWindowHelper(false);
84 }
85
86 NS_IMETHODIMP
SetScreenId(uint32_t aScreenId)87 nsAppShellService::SetScreenId(uint32_t aScreenId) {
88 mScreenId = aScreenId;
89 return NS_OK;
90 }
91
EnsurePrivateHiddenWindow()92 void nsAppShellService::EnsurePrivateHiddenWindow() {
93 if (!mHiddenPrivateWindow) {
94 CreateHiddenWindowHelper(true);
95 }
96 }
97
CreateHiddenWindowHelper(bool aIsPrivate)98 nsresult nsAppShellService::CreateHiddenWindowHelper(bool aIsPrivate) {
99 nsresult rv;
100 int32_t initialHeight = 100, initialWidth = 100;
101
102 #ifdef XP_MACOSX
103 uint32_t chromeMask = 0;
104 nsAutoCString prefVal;
105 rv = Preferences::GetCString("browser.hiddenWindowChromeURL", prefVal);
106 const char* hiddenWindowURL =
107 NS_SUCCEEDED(rv) ? prefVal.get() : DEFAULT_HIDDENWINDOW_URL;
108 if (aIsPrivate) {
109 hiddenWindowURL = DEFAULT_HIDDENWINDOW_URL;
110 } else {
111 mApplicationProvidedHiddenWindow = prefVal.get() ? true : false;
112 }
113 #else
114 static const char hiddenWindowURL[] = DEFAULT_HIDDENWINDOW_URL;
115 uint32_t chromeMask = nsIWebBrowserChrome::CHROME_ALL;
116 #endif
117
118 nsCOMPtr<nsIURI> url;
119 rv = NS_NewURI(getter_AddRefs(url), hiddenWindowURL);
120 NS_ENSURE_SUCCESS(rv, rv);
121
122 if (aIsPrivate) {
123 chromeMask |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
124 }
125
126 RefPtr<nsWebShellWindow> newWindow;
127 rv =
128 JustCreateTopWindow(nullptr, url, chromeMask, initialWidth, initialHeight,
129 true, nullptr, nullptr, getter_AddRefs(newWindow));
130 NS_ENSURE_SUCCESS(rv, rv);
131
132 nsCOMPtr<nsIDocShell> docShell;
133 newWindow->GetDocShell(getter_AddRefs(docShell));
134 if (docShell) {
135 docShell->SetIsActive(false);
136 if (aIsPrivate) {
137 docShell->SetAffectPrivateSessionLifetime(false);
138 }
139 }
140
141 if (aIsPrivate) {
142 mHiddenPrivateWindow.swap(newWindow);
143 } else {
144 mHiddenWindow.swap(newWindow);
145 }
146
147 return NS_OK;
148 }
149
150 NS_IMETHODIMP
DestroyHiddenWindow()151 nsAppShellService::DestroyHiddenWindow() {
152 if (mHiddenWindow) {
153 mHiddenWindow->Destroy();
154
155 mHiddenWindow = nullptr;
156 }
157
158 if (mHiddenPrivateWindow) {
159 mHiddenPrivateWindow->Destroy();
160
161 mHiddenPrivateWindow = nullptr;
162 }
163
164 return NS_OK;
165 }
166
167 /*
168 * Create a new top level window and display the given URL within it...
169 */
170 NS_IMETHODIMP
CreateTopLevelWindow(nsIXULWindow * aParent,nsIURI * aUrl,uint32_t aChromeMask,int32_t aInitialWidth,int32_t aInitialHeight,nsITabParent * aOpeningTab,mozIDOMWindowProxy * aOpenerWindow,nsIXULWindow ** aResult)171 nsAppShellService::CreateTopLevelWindow(
172 nsIXULWindow* aParent, nsIURI* aUrl, uint32_t aChromeMask,
173 int32_t aInitialWidth, int32_t aInitialHeight, nsITabParent* aOpeningTab,
174 mozIDOMWindowProxy* aOpenerWindow, nsIXULWindow** aResult)
175
176 {
177 nsresult rv;
178
179 StartupTimeline::RecordOnce(StartupTimeline::CREATE_TOP_LEVEL_WINDOW);
180
181 RefPtr<nsWebShellWindow> newWindow;
182 rv = JustCreateTopWindow(aParent, aUrl, aChromeMask, aInitialWidth,
183 aInitialHeight, false, aOpeningTab, aOpenerWindow,
184 getter_AddRefs(newWindow));
185 newWindow.forget(aResult);
186
187 if (NS_SUCCEEDED(rv)) {
188 // the addref resulting from this is the owning addref for this window
189 RegisterTopLevelWindow(*aResult);
190 nsCOMPtr<nsIXULWindow> parent;
191 if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) parent = aParent;
192 (*aResult)->SetZLevel(CalculateWindowZLevel(parent, aChromeMask));
193 }
194
195 return rv;
196 }
197
198 /*
199 * This class provides a stub implementation of nsIWebBrowserChrome2, as needed
200 * by nsAppShellService::CreateWindowlessBrowser
201 */
202 class WebBrowserChrome2Stub : public nsIWebBrowserChrome2,
203 public nsIEmbeddingSiteWindow,
204 public nsIInterfaceRequestor,
205 public nsSupportsWeakReference {
206 protected:
207 nsCOMPtr<nsIWebBrowser> mBrowser;
~WebBrowserChrome2Stub()208 virtual ~WebBrowserChrome2Stub() {}
209
210 public:
WebBrowserChrome2Stub(nsIWebBrowser * aBrowser)211 explicit WebBrowserChrome2Stub(nsIWebBrowser* aBrowser)
212 : mBrowser(aBrowser) {}
213 NS_DECL_ISUPPORTS
214 NS_DECL_NSIWEBBROWSERCHROME
215 NS_DECL_NSIWEBBROWSERCHROME2
216 NS_DECL_NSIINTERFACEREQUESTOR
217 NS_DECL_NSIEMBEDDINGSITEWINDOW
218 };
219
220 NS_INTERFACE_MAP_BEGIN(WebBrowserChrome2Stub)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,nsIWebBrowserChrome)221 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome)
222 NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
223 NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2)
224 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
225 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
226 NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
227 NS_INTERFACE_MAP_END
228
229 NS_IMPL_ADDREF(WebBrowserChrome2Stub)
230 NS_IMPL_RELEASE(WebBrowserChrome2Stub)
231
232 NS_IMETHODIMP
233 WebBrowserChrome2Stub::SetStatus(uint32_t aStatusType,
234 const char16_t* aStatus) {
235 return NS_OK;
236 }
237
238 NS_IMETHODIMP
GetWebBrowser(nsIWebBrowser ** aWebBrowser)239 WebBrowserChrome2Stub::GetWebBrowser(nsIWebBrowser** aWebBrowser) {
240 NS_NOTREACHED("WebBrowserChrome2Stub::GetWebBrowser is not supported");
241 return NS_ERROR_NOT_IMPLEMENTED;
242 }
243
244 NS_IMETHODIMP
SetWebBrowser(nsIWebBrowser * aWebBrowser)245 WebBrowserChrome2Stub::SetWebBrowser(nsIWebBrowser* aWebBrowser) {
246 NS_NOTREACHED("WebBrowserChrome2Stub::SetWebBrowser is not supported");
247 return NS_ERROR_NOT_IMPLEMENTED;
248 }
249
250 NS_IMETHODIMP
GetChromeFlags(uint32_t * aChromeFlags)251 WebBrowserChrome2Stub::GetChromeFlags(uint32_t* aChromeFlags) {
252 *aChromeFlags = 0;
253 return NS_OK;
254 }
255
256 NS_IMETHODIMP
SetChromeFlags(uint32_t aChromeFlags)257 WebBrowserChrome2Stub::SetChromeFlags(uint32_t aChromeFlags) {
258 NS_NOTREACHED("WebBrowserChrome2Stub::SetChromeFlags is not supported");
259 return NS_ERROR_NOT_IMPLEMENTED;
260 }
261
262 NS_IMETHODIMP
DestroyBrowserWindow()263 WebBrowserChrome2Stub::DestroyBrowserWindow() {
264 NS_NOTREACHED("WebBrowserChrome2Stub::DestroyBrowserWindow is not supported");
265 return NS_ERROR_NOT_IMPLEMENTED;
266 }
267
268 NS_IMETHODIMP
SizeBrowserTo(int32_t aCX,int32_t aCY)269 WebBrowserChrome2Stub::SizeBrowserTo(int32_t aCX, int32_t aCY) {
270 NS_NOTREACHED("WebBrowserChrome2Stub::SizeBrowserTo is not supported");
271 return NS_ERROR_NOT_IMPLEMENTED;
272 }
273
274 NS_IMETHODIMP
ShowAsModal()275 WebBrowserChrome2Stub::ShowAsModal() {
276 NS_NOTREACHED("WebBrowserChrome2Stub::ShowAsModal is not supported");
277 return NS_ERROR_NOT_IMPLEMENTED;
278 }
279
280 NS_IMETHODIMP
IsWindowModal(bool * aResult)281 WebBrowserChrome2Stub::IsWindowModal(bool* aResult) {
282 *aResult = false;
283 return NS_OK;
284 }
285
286 NS_IMETHODIMP
ExitModalEventLoop(nsresult aStatus)287 WebBrowserChrome2Stub::ExitModalEventLoop(nsresult aStatus) {
288 NS_NOTREACHED("WebBrowserChrome2Stub::ExitModalEventLoop is not supported");
289 return NS_ERROR_NOT_IMPLEMENTED;
290 }
291
292 NS_IMETHODIMP
SetStatusWithContext(uint32_t aStatusType,const nsAString & aStatusText,nsISupports * aStatusContext)293 WebBrowserChrome2Stub::SetStatusWithContext(uint32_t aStatusType,
294 const nsAString& aStatusText,
295 nsISupports* aStatusContext) {
296 return NS_OK;
297 }
298
299 NS_IMETHODIMP
GetInterface(const nsIID & aIID,void ** aSink)300 WebBrowserChrome2Stub::GetInterface(const nsIID& aIID, void** aSink) {
301 return QueryInterface(aIID, aSink);
302 }
303
304 // nsIEmbeddingSiteWindow impl
305 NS_IMETHODIMP
GetDimensions(uint32_t flags,int32_t * x,int32_t * y,int32_t * cx,int32_t * cy)306 WebBrowserChrome2Stub::GetDimensions(uint32_t flags, int32_t* x, int32_t* y,
307 int32_t* cx, int32_t* cy) {
308 if (x) {
309 *x = 0;
310 }
311
312 if (y) {
313 *y = 0;
314 }
315
316 if (cx) {
317 *cx = 0;
318 }
319
320 if (cy) {
321 *cy = 0;
322 }
323
324 return NS_OK;
325 }
326
327 NS_IMETHODIMP
SetDimensions(uint32_t flags,int32_t x,int32_t y,int32_t cx,int32_t cy)328 WebBrowserChrome2Stub::SetDimensions(uint32_t flags, int32_t x, int32_t y,
329 int32_t cx, int32_t cy) {
330 nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser);
331 NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
332 window->SetSize(cx, cy, true);
333 return NS_OK;
334 }
335
336 NS_IMETHODIMP
SetFocus()337 WebBrowserChrome2Stub::SetFocus() { return NS_ERROR_NOT_IMPLEMENTED; }
338
339 NS_IMETHODIMP
GetVisibility(bool * aVisibility)340 WebBrowserChrome2Stub::GetVisibility(bool* aVisibility) {
341 return NS_ERROR_NOT_IMPLEMENTED;
342 }
343 NS_IMETHODIMP
SetVisibility(bool aVisibility)344 WebBrowserChrome2Stub::SetVisibility(bool aVisibility) {
345 return NS_ERROR_NOT_IMPLEMENTED;
346 }
347
348 NS_IMETHODIMP
GetTitle(nsAString & aTitle)349 WebBrowserChrome2Stub::GetTitle(nsAString& aTitle) {
350 return NS_ERROR_NOT_IMPLEMENTED;
351 }
352 NS_IMETHODIMP
SetTitle(const nsAString & aTitle)353 WebBrowserChrome2Stub::SetTitle(const nsAString& aTitle) {
354 return NS_ERROR_NOT_IMPLEMENTED;
355 }
356
357 NS_IMETHODIMP
GetSiteWindow(void ** aSiteWindow)358 WebBrowserChrome2Stub::GetSiteWindow(void** aSiteWindow) {
359 return NS_ERROR_NOT_IMPLEMENTED;
360 }
361
362 NS_IMETHODIMP
Blur()363 WebBrowserChrome2Stub::Blur() { return NS_ERROR_NOT_IMPLEMENTED; }
364
365 class BrowserDestroyer final : public Runnable {
366 public:
BrowserDestroyer(nsIWebBrowser * aBrowser,nsISupports * aContainer)367 BrowserDestroyer(nsIWebBrowser* aBrowser, nsISupports* aContainer)
368 : mozilla::Runnable("BrowserDestroyer"),
369 mBrowser(aBrowser),
370 mContainer(aContainer) {}
371
372 NS_IMETHOD
Run()373 Run() override {
374 // Explicitly destroy the browser, in case this isn't the last reference.
375 nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser);
376 return window->Destroy();
377 }
378
379 protected:
~BrowserDestroyer()380 virtual ~BrowserDestroyer() {}
381
382 private:
383 nsCOMPtr<nsIWebBrowser> mBrowser;
384 nsCOMPtr<nsISupports> mContainer;
385 };
386
387 // This is the "stub" we return from CreateWindowlessBrowser - it exists
388 // to manage the lifetimes of the nsIWebBrowser and container window.
389 // In particular, it keeps a strong reference to both, to prevent them from
390 // being collected while this object remains alive, and ensures that they
391 // aren't destroyed when it's not safe to run scripts.
392 class WindowlessBrowser final : public nsIWindowlessBrowser,
393 public nsIInterfaceRequestor {
394 public:
WindowlessBrowser(nsIWebBrowser * aBrowser,nsISupports * aContainer)395 WindowlessBrowser(nsIWebBrowser* aBrowser, nsISupports* aContainer)
396 : mBrowser(aBrowser), mContainer(aContainer), mClosed(false) {
397 mWebNavigation = do_QueryInterface(aBrowser);
398 mInterfaceRequestor = do_QueryInterface(aBrowser);
399 }
400 NS_DECL_ISUPPORTS
NS_FORWARD_SAFE_NSIWEBNAVIGATION(mWebNavigation)401 NS_FORWARD_SAFE_NSIWEBNAVIGATION(mWebNavigation)
402 NS_FORWARD_SAFE_NSIINTERFACEREQUESTOR(mInterfaceRequestor)
403
404 NS_IMETHOD
405 Close() override {
406 NS_ENSURE_TRUE(!mClosed, NS_ERROR_UNEXPECTED);
407 NS_ASSERTION(
408 nsContentUtils::IsSafeToRunScript(),
409 "WindowlessBrowser::Close called when not safe to run scripts");
410
411 mClosed = true;
412
413 mWebNavigation = nullptr;
414 mInterfaceRequestor = nullptr;
415
416 nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser);
417 return window->Destroy();
418 }
419
420 protected:
~WindowlessBrowser()421 virtual ~WindowlessBrowser() {
422 if (mClosed) {
423 return;
424 }
425
426 NS_WARNING("Windowless browser was not closed prior to destruction");
427
428 // The docshell destructor needs to dispatch events, and can only run
429 // when it's safe to run scripts. If this was triggered by GC, it may
430 // not always be safe to run scripts, in which cases we need to delay
431 // destruction until it is.
432 nsCOMPtr<nsIRunnable> runnable = new BrowserDestroyer(mBrowser, mContainer);
433 nsContentUtils::AddScriptRunner(runnable);
434 }
435
436 private:
437 nsCOMPtr<nsIWebBrowser> mBrowser;
438 nsCOMPtr<nsIWebNavigation> mWebNavigation;
439 nsCOMPtr<nsIInterfaceRequestor> mInterfaceRequestor;
440 // we don't use the container but just hold a reference to it.
441 nsCOMPtr<nsISupports> mContainer;
442
443 bool mClosed;
444 };
445
NS_IMPL_ISUPPORTS(WindowlessBrowser,nsIWindowlessBrowser,nsIWebNavigation,nsIInterfaceRequestor)446 NS_IMPL_ISUPPORTS(WindowlessBrowser, nsIWindowlessBrowser, nsIWebNavigation,
447 nsIInterfaceRequestor)
448
449 NS_IMETHODIMP
450 nsAppShellService::CreateWindowlessBrowser(bool aIsChrome,
451 nsIWindowlessBrowser** aResult) {
452 /* First, we create an instance of nsWebBrowser. Instances of this class have
453 * an associated doc shell, which is what we're interested in.
454 */
455 nsCOMPtr<nsIWebBrowser> browser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID);
456 if (!browser) {
457 NS_ERROR("Couldn't create instance of nsWebBrowser!");
458 return NS_ERROR_FAILURE;
459 }
460
461 /* Next, we set the container window for our instance of nsWebBrowser. Since
462 * we don't actually have a window, we instead set the container window to be
463 * an instance of WebBrowserChrome2Stub, which provides a stub implementation
464 * of nsIWebBrowserChrome2.
465 */
466 RefPtr<WebBrowserChrome2Stub> stub = new WebBrowserChrome2Stub(browser);
467 browser->SetContainerWindow(stub);
468
469 nsCOMPtr<nsIWebNavigation> navigation = do_QueryInterface(browser);
470
471 nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(navigation);
472 item->SetItemType(aIsChrome ? nsIDocShellTreeItem::typeChromeWrapper
473 : nsIDocShellTreeItem::typeContentWrapper);
474
475 /* A windowless web browser doesn't have an associated OS level window. To
476 * accomplish this, we initialize the window associated with our instance of
477 * nsWebBrowser with an instance of HeadlessWidget/PuppetWidget, which provide
478 * a stub implementation of nsIWidget.
479 */
480 nsCOMPtr<nsIWidget> widget;
481 if (gfxPlatform::IsHeadless()) {
482 widget = nsIWidget::CreateHeadlessWidget();
483 } else {
484 widget = nsIWidget::CreatePuppetWidget(nullptr);
485 }
486 if (!widget) {
487 NS_ERROR("Couldn't create instance of stub widget");
488 return NS_ERROR_FAILURE;
489 }
490 nsresult rv =
491 widget->Create(nullptr, 0, LayoutDeviceIntRect(0, 0, 0, 0), nullptr);
492 NS_ENSURE_SUCCESS(rv, rv);
493 nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(navigation);
494 window->InitWindow(0, widget, 0, 0, 0, 0);
495 window->Create();
496
497 nsISupports* isstub = NS_ISUPPORTS_CAST(nsIWebBrowserChrome2*, stub);
498 RefPtr<nsIWindowlessBrowser> result = new WindowlessBrowser(browser, isstub);
499 nsCOMPtr<nsIDocShell> docshell = do_GetInterface(result);
500 docshell->SetInvisible(true);
501
502 result.forget(aResult);
503 return NS_OK;
504 }
505
CalculateWindowZLevel(nsIXULWindow * aParent,uint32_t aChromeMask)506 uint32_t nsAppShellService::CalculateWindowZLevel(nsIXULWindow* aParent,
507 uint32_t aChromeMask) {
508 uint32_t zLevel;
509
510 zLevel = nsIXULWindow::normalZ;
511 if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RAISED)
512 zLevel = nsIXULWindow::raisedZ;
513 else if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_LOWERED)
514 zLevel = nsIXULWindow::loweredZ;
515
516 #ifdef XP_MACOSX
517 /* Platforms on which modal windows are always application-modal, not
518 window-modal (that's just the Mac, right?) want modal windows to
519 be stacked on top of everyone else.
520
521 On Mac OS X, bind modality to parent window instead of app (ala Mac OS 9)
522 */
523 uint32_t modalDepMask =
524 nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT;
525 if (aParent && (aChromeMask & modalDepMask)) {
526 aParent->GetZLevel(&zLevel);
527 }
528 #else
529 /* Platforms with native support for dependent windows (that's everyone
530 but pre-Mac OS X, right?) know how to stack dependent windows. On these
531 platforms, give the dependent window the same level as its parent,
532 so we won't try to override the normal platform behaviour. */
533 if ((aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) && aParent)
534 aParent->GetZLevel(&zLevel);
535 #endif
536
537 return zLevel;
538 }
539
540 #ifdef XP_WIN
541 /*
542 * Checks to see if any existing window is currently in fullscreen mode.
543 */
CheckForFullscreenWindow()544 static bool CheckForFullscreenWindow() {
545 nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
546 if (!wm) return false;
547
548 nsCOMPtr<nsISimpleEnumerator> windowList;
549 wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList));
550 if (!windowList) return false;
551
552 for (;;) {
553 bool more = false;
554 windowList->HasMoreElements(&more);
555 if (!more) return false;
556
557 nsCOMPtr<nsISupports> supportsWindow;
558 windowList->GetNext(getter_AddRefs(supportsWindow));
559 nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow));
560 if (baseWin) {
561 nsCOMPtr<nsIWidget> widget;
562 baseWin->GetMainWidget(getter_AddRefs(widget));
563 if (widget && widget->SizeMode() == nsSizeMode_Fullscreen) {
564 return true;
565 }
566 }
567 }
568 return false;
569 }
570 #endif
571
572 /*
573 * Just do the window-making part of CreateTopLevelWindow
574 */
JustCreateTopWindow(nsIXULWindow * aParent,nsIURI * aUrl,uint32_t aChromeMask,int32_t aInitialWidth,int32_t aInitialHeight,bool aIsHiddenWindow,nsITabParent * aOpeningTab,mozIDOMWindowProxy * aOpenerWindow,nsWebShellWindow ** aResult)575 nsresult nsAppShellService::JustCreateTopWindow(
576 nsIXULWindow* aParent, nsIURI* aUrl, uint32_t aChromeMask,
577 int32_t aInitialWidth, int32_t aInitialHeight, bool aIsHiddenWindow,
578 nsITabParent* aOpeningTab, mozIDOMWindowProxy* aOpenerWindow,
579 nsWebShellWindow** aResult) {
580 *aResult = nullptr;
581 NS_ENSURE_STATE(!mXPCOMWillShutDown);
582
583 nsCOMPtr<nsIXULWindow> parent;
584 if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) parent = aParent;
585
586 RefPtr<nsWebShellWindow> window = new nsWebShellWindow(aChromeMask);
587
588 #ifdef XP_WIN
589 // If the parent is currently fullscreen, tell the child to ignore persisted
590 // full screen states. This way new browser windows open on top of fullscreen
591 // windows normally.
592 if (window && CheckForFullscreenWindow()) window->IgnoreXULSizeMode(true);
593 #endif
594
595 nsWidgetInitData widgetInitData;
596
597 if (aIsHiddenWindow)
598 widgetInitData.mWindowType = eWindowType_invisible;
599 else
600 widgetInitData.mWindowType =
601 aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG
602 ? eWindowType_dialog
603 : eWindowType_toplevel;
604
605 if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)
606 widgetInitData.mWindowType = eWindowType_popup;
607
608 if (aChromeMask & nsIWebBrowserChrome::CHROME_SUPPRESS_ANIMATION)
609 widgetInitData.mIsAnimationSuppressed = true;
610
611 #ifdef XP_MACOSX
612 // Mac OS X sheet support
613 // Adding CHROME_OPENAS_CHROME to sheetMask makes modal windows opened from
614 // nsGlobalWindow::ShowModalDialog() be dialogs (not sheets), while modal
615 // windows opened from nsPromptService::DoDialog() still are sheets. This
616 // fixes bmo bug 395465 (see nsCocoaWindow::StandardCreate() and
617 // nsCocoaWindow::SetModal()).
618 uint32_t sheetMask = nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
619 nsIWebBrowserChrome::CHROME_MODAL |
620 nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
621 if (parent && (parent != mHiddenWindow && parent != mHiddenPrivateWindow) &&
622 ((aChromeMask & sheetMask) == sheetMask)) {
623 widgetInitData.mWindowType = eWindowType_sheet;
624 }
625 #endif
626
627 #if defined(XP_WIN)
628 if (widgetInitData.mWindowType == eWindowType_toplevel ||
629 widgetInitData.mWindowType == eWindowType_dialog)
630 widgetInitData.clipChildren = true;
631 #endif
632
633 // note default chrome overrides other OS chrome settings, but
634 // not internal chrome
635 if (aChromeMask & nsIWebBrowserChrome::CHROME_DEFAULT)
636 widgetInitData.mBorderStyle = eBorderStyle_default;
637 else if ((aChromeMask & nsIWebBrowserChrome::CHROME_ALL) ==
638 nsIWebBrowserChrome::CHROME_ALL)
639 widgetInitData.mBorderStyle = eBorderStyle_all;
640 else {
641 widgetInitData.mBorderStyle = eBorderStyle_none; // assumes none == 0x00
642 if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_BORDERS)
643 widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
644 widgetInitData.mBorderStyle | eBorderStyle_border);
645 if (aChromeMask & nsIWebBrowserChrome::CHROME_TITLEBAR)
646 widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
647 widgetInitData.mBorderStyle | eBorderStyle_title);
648 if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_CLOSE)
649 widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
650 widgetInitData.mBorderStyle | eBorderStyle_close);
651 if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
652 widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
653 widgetInitData.mBorderStyle | eBorderStyle_resizeh);
654 // only resizable windows get the maximize button (but not dialogs)
655 if (!(aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG))
656 widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
657 widgetInitData.mBorderStyle | eBorderStyle_maximize);
658 }
659 // all windows (except dialogs) get minimize buttons and the system menu
660 if (!(aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG))
661 widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
662 widgetInitData.mBorderStyle | eBorderStyle_minimize |
663 eBorderStyle_menu);
664 // but anyone can explicitly ask for a minimize button
665 if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_MIN) {
666 widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
667 widgetInitData.mBorderStyle | eBorderStyle_minimize);
668 }
669 }
670
671 if (aInitialWidth == nsIAppShellService::SIZE_TO_CONTENT ||
672 aInitialHeight == nsIAppShellService::SIZE_TO_CONTENT) {
673 aInitialWidth = 1;
674 aInitialHeight = 1;
675 window->SetIntrinsicallySized(true);
676 }
677
678 bool center = aChromeMask & nsIWebBrowserChrome::CHROME_CENTER_SCREEN;
679
680 widgetInitData.mRTL = LocaleService::GetInstance()->IsAppLocaleRTL();
681
682 nsresult rv = window->Initialize(
683 parent, center ? aParent : nullptr, aUrl, aInitialWidth, aInitialHeight,
684 aIsHiddenWindow, aOpeningTab, aOpenerWindow, widgetInitData);
685
686 NS_ENSURE_SUCCESS(rv, rv);
687
688 // Enforce the Private Browsing autoStart pref first.
689 bool isPrivateBrowsingWindow =
690 Preferences::GetBool("browser.privatebrowsing.autostart");
691 bool isUsingRemoteTabs = mozilla::BrowserTabsRemoteAutostart();
692
693 if (aChromeMask & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) {
694 // Caller requested a private window
695 isPrivateBrowsingWindow = true;
696 }
697 if (aChromeMask & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) {
698 isUsingRemoteTabs = true;
699 }
700
701 nsCOMPtr<mozIDOMWindowProxy> domWin = do_GetInterface(aParent);
702 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(domWin);
703 nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(webNav);
704
705 if (!isPrivateBrowsingWindow && parentContext) {
706 // Ensure that we propagate any existing private browsing status
707 // from the parent, even if it will not actually be used
708 // as a parent value.
709 isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
710 }
711
712 if (parentContext) {
713 isUsingRemoteTabs = parentContext->UseRemoteTabs();
714 }
715
716 nsCOMPtr<mozIDOMWindowProxy> newDomWin =
717 do_GetInterface(NS_ISUPPORTS_CAST(nsIBaseWindow*, window));
718 nsCOMPtr<nsIWebNavigation> newWebNav = do_GetInterface(newDomWin);
719 nsCOMPtr<nsILoadContext> thisContext = do_GetInterface(newWebNav);
720 if (thisContext) {
721 thisContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
722 thisContext->SetRemoteTabs(isUsingRemoteTabs);
723 }
724
725 window.forget(aResult);
726 if (parent) parent->AddChildWindow(*aResult);
727
728 if (center) rv = (*aResult)->Center(parent, parent ? false : true, false);
729
730 return rv;
731 }
732
733 NS_IMETHODIMP
GetHiddenWindow(nsIXULWindow ** aWindow)734 nsAppShellService::GetHiddenWindow(nsIXULWindow** aWindow) {
735 NS_ENSURE_ARG_POINTER(aWindow);
736
737 *aWindow = mHiddenWindow;
738 NS_IF_ADDREF(*aWindow);
739 return *aWindow ? NS_OK : NS_ERROR_FAILURE;
740 }
741
742 NS_IMETHODIMP
GetHiddenDOMWindow(mozIDOMWindowProxy ** aWindow)743 nsAppShellService::GetHiddenDOMWindow(mozIDOMWindowProxy** aWindow) {
744 nsresult rv;
745 nsCOMPtr<nsIDocShell> docShell;
746 NS_ENSURE_TRUE(mHiddenWindow, NS_ERROR_FAILURE);
747
748 rv = mHiddenWindow->GetDocShell(getter_AddRefs(docShell));
749 NS_ENSURE_SUCCESS(rv, rv);
750 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
751
752 nsCOMPtr<nsPIDOMWindowOuter> hiddenDOMWindow(docShell->GetWindow());
753 hiddenDOMWindow.forget(aWindow);
754 return *aWindow ? NS_OK : NS_ERROR_FAILURE;
755 }
756
757 NS_IMETHODIMP
GetHiddenPrivateWindow(nsIXULWindow ** aWindow)758 nsAppShellService::GetHiddenPrivateWindow(nsIXULWindow** aWindow) {
759 NS_ENSURE_ARG_POINTER(aWindow);
760
761 EnsurePrivateHiddenWindow();
762
763 *aWindow = mHiddenPrivateWindow;
764 NS_IF_ADDREF(*aWindow);
765 return *aWindow ? NS_OK : NS_ERROR_FAILURE;
766 }
767
768 NS_IMETHODIMP
GetHiddenPrivateDOMWindow(mozIDOMWindowProxy ** aWindow)769 nsAppShellService::GetHiddenPrivateDOMWindow(mozIDOMWindowProxy** aWindow) {
770 EnsurePrivateHiddenWindow();
771
772 nsresult rv;
773 nsCOMPtr<nsIDocShell> docShell;
774 NS_ENSURE_TRUE(mHiddenPrivateWindow, NS_ERROR_FAILURE);
775
776 rv = mHiddenPrivateWindow->GetDocShell(getter_AddRefs(docShell));
777 NS_ENSURE_SUCCESS(rv, rv);
778 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
779
780 nsCOMPtr<nsPIDOMWindowOuter> hiddenPrivateDOMWindow(docShell->GetWindow());
781 hiddenPrivateDOMWindow.forget(aWindow);
782 return *aWindow ? NS_OK : NS_ERROR_FAILURE;
783 }
784
785 NS_IMETHODIMP
GetHasHiddenPrivateWindow(bool * aHasPrivateWindow)786 nsAppShellService::GetHasHiddenPrivateWindow(bool* aHasPrivateWindow) {
787 NS_ENSURE_ARG_POINTER(aHasPrivateWindow);
788
789 *aHasPrivateWindow = !!mHiddenPrivateWindow;
790 return NS_OK;
791 }
792
793 NS_IMETHODIMP
GetApplicationProvidedHiddenWindow(bool * aAPHW)794 nsAppShellService::GetApplicationProvidedHiddenWindow(bool* aAPHW) {
795 *aAPHW = mApplicationProvidedHiddenWindow;
796 return NS_OK;
797 }
798
799 /*
800 * Register a new top level window (created elsewhere)
801 */
802 NS_IMETHODIMP
RegisterTopLevelWindow(nsIXULWindow * aWindow)803 nsAppShellService::RegisterTopLevelWindow(nsIXULWindow* aWindow) {
804 NS_ENSURE_ARG_POINTER(aWindow);
805
806 nsCOMPtr<nsIDocShell> docShell;
807 aWindow->GetDocShell(getter_AddRefs(docShell));
808 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
809
810 nsCOMPtr<nsPIDOMWindowOuter> domWindow(docShell->GetWindow());
811 NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
812 domWindow->SetInitialPrincipalToSubject();
813
814 // tell the window mediator about the new window
815 nsCOMPtr<nsIWindowMediator> mediator(
816 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
817 NS_ASSERTION(mediator, "Couldn't get window mediator.");
818
819 if (mediator) mediator->RegisterWindow(aWindow);
820
821 // tell the window watcher about the new window
822 nsCOMPtr<nsPIWindowWatcher> wwatcher(
823 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
824 NS_ASSERTION(wwatcher, "No windowwatcher?");
825 if (wwatcher && domWindow) {
826 wwatcher->AddWindow(domWindow, 0);
827 }
828
829 // an ongoing attempt to quit is stopped by a newly opened window
830 nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
831 NS_ASSERTION(obssvc, "Couldn't get observer service.");
832
833 if (obssvc) {
834 obssvc->NotifyObservers(aWindow, "xul-window-registered", nullptr);
835 nsXULWindow* xulWindow = static_cast<nsXULWindow*>(aWindow);
836 xulWindow->WasRegistered();
837 }
838
839 return NS_OK;
840 }
841
842 NS_IMETHODIMP
UnregisterTopLevelWindow(nsIXULWindow * aWindow)843 nsAppShellService::UnregisterTopLevelWindow(nsIXULWindow* aWindow) {
844 if (mXPCOMShuttingDown) {
845 /* return an error code in order to:
846 - avoid doing anything with other member variables while we are in
847 the destructor
848 - notify the caller not to release the AppShellService after
849 unregistering the window
850 (we don't want to be deleted twice consecutively to
851 mHiddenWindow->Destroy() in our destructor)
852 */
853 return NS_ERROR_FAILURE;
854 }
855
856 NS_ENSURE_ARG_POINTER(aWindow);
857
858 if (aWindow == mHiddenWindow) {
859 // CreateHiddenWindow() does not register the window, so we're done.
860 return NS_OK;
861 }
862 if (aWindow == mHiddenPrivateWindow) {
863 // CreateHiddenWindow() does not register the window, so we're done.
864 return NS_OK;
865 }
866
867 // tell the window mediator
868 nsCOMPtr<nsIWindowMediator> mediator(
869 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
870 NS_ASSERTION(mediator, "Couldn't get window mediator. Doing xpcom shutdown?");
871
872 if (mediator) mediator->UnregisterWindow(aWindow);
873
874 // tell the window watcher
875 nsCOMPtr<nsPIWindowWatcher> wwatcher(
876 do_GetService(NS_WINDOWWATCHER_CONTRACTID));
877 NS_ASSERTION(wwatcher, "Couldn't get windowwatcher, doing xpcom shutdown?");
878 if (wwatcher) {
879 nsCOMPtr<nsIDocShell> docShell;
880 aWindow->GetDocShell(getter_AddRefs(docShell));
881 if (docShell) {
882 nsCOMPtr<nsPIDOMWindowOuter> domWindow(docShell->GetWindow());
883 if (domWindow) wwatcher->RemoveWindow(domWindow);
884 }
885 }
886
887 return NS_OK;
888 }
889
890 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)891 nsAppShellService::Observe(nsISupports* aSubject, const char* aTopic,
892 const char16_t* aData) {
893 if (!strcmp(aTopic, "xpcom-will-shutdown")) {
894 mXPCOMWillShutDown = true;
895 } else if (!strcmp(aTopic, "xpcom-shutdown")) {
896 mXPCOMShuttingDown = true;
897 if (mHiddenWindow) {
898 mHiddenWindow->Destroy();
899 }
900 if (mHiddenPrivateWindow) {
901 mHiddenPrivateWindow->Destroy();
902 }
903 } else {
904 NS_ERROR("Unexpected observer topic!");
905 }
906
907 return NS_OK;
908 }
909
910 NS_IMETHODIMP
StartEventLoopLagTracking(bool * aResult)911 nsAppShellService::StartEventLoopLagTracking(bool* aResult) {
912 #ifdef MOZ_INSTRUMENT_EVENT_LOOP
913 *aResult = mozilla::InitEventTracing(true);
914 #endif
915 return NS_OK;
916 }
917
918 NS_IMETHODIMP
StopEventLoopLagTracking()919 nsAppShellService::StopEventLoopLagTracking() {
920 #ifdef MOZ_INSTRUMENT_EVENT_LOOP
921 mozilla::ShutdownEventTracing();
922 #endif
923 return NS_OK;
924 }
925