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