nsAppShellService.cpp 29.4 KB
Newer Older
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
3
4
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6

#include "nsIAppShellService.h"
7
#include "nsNetUtil.h"
8
9
#include "nsIObserverService.h"
#include "nsIObserver.h"
10
#include "nsIXULRuntime.h"
11

12
13
#include "nsIWindowMediator.h"
#include "nsPIWindowWatcher.h"
14
#include "nsPIDOMWindow.h"
15
#include "AppWindow.h"
16

17
#include "nsWidgetInitData.h"
18
#include "nsWidgetsCID.h"
19
#include "nsIWidget.h"
20
#include "nsIEmbeddingSiteWindow.h"
21

22
#include "nsAppDirectoryServiceDefs.h"
23
#include "nsAppShellService.h"
24
#include "nsContentUtils.h"
25
#include "nsDirectoryServiceUtils.h"
26
#include "nsThreadUtils.h"
27
28
#include "nsILoadContext.h"
#include "nsIWebNavigation.h"
29
#include "nsIWindowlessBrowser.h"
30

31
#include "mozilla/Attributes.h"
32
#include "mozilla/Preferences.h"
33
#include "mozilla/Services.h"
34
#include "mozilla/StartupTimeline.h"
35
#include "mozilla/StaticPrefs_fission.h"
36
#include "mozilla/intl/LocaleService.h"
37
#include "mozilla/dom/BrowsingContext.h"
38

39
40
41
#include "nsEmbedCID.h"
#include "nsIWebBrowser.h"
#include "nsIDocShell.h"
42
#include "gfxPlatform.h"
43

44
#include "nsWebBrowser.h"
45
46
#include "nsDocShell.h"
#include "nsDocShellLoadState.h"
47

48
#ifdef MOZ_INSTRUMENT_EVENT_LOOP
49
#  include "EventTracer.h"
50
#endif
51

52
using namespace mozilla;
53
using mozilla::dom::BrowsingContext;
54
using mozilla::intl::LocaleService;
55

56
// Default URL for the hidden window, can be overridden by a pref on Mac
57
#define DEFAULT_HIDDENWINDOW_URL "resource://gre-resources/hiddenWindow.html"
58

59
class nsIAppShell;
60

61
62
63
64
65
66
nsAppShellService::nsAppShellService()
    : mXPCOMWillShutDown(false),
      mXPCOMShuttingDown(false),
      mModalWindowCount(0),
      mApplicationProvidedHiddenWindow(false),
      mScreenId(0) {
67
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
68

69
  if (obs) {
70
71
    obs->AddObserver(this, "xpcom-will-shutdown", false);
    obs->AddObserver(this, "xpcom-shutdown", false);
72
  }
73
74
}

75
nsAppShellService::~nsAppShellService() {}
76
77
78
79

/*
 * Implement the nsISupports methods...
 */
80
NS_IMPL_ISUPPORTS(nsAppShellService, nsIAppShellService, nsIObserver)
81

82
NS_IMETHODIMP
83
nsAppShellService::SetScreenId(uint32_t aScreenId) {
84
85
86
87
  mScreenId = aScreenId;
  return NS_OK;
}

88
89
void nsAppShellService::EnsureHiddenWindow() {
  if (!mHiddenWindow) {
90
    (void)CreateHiddenWindow();
91
92
93
  }
}

94
95
NS_IMETHODIMP
nsAppShellService::CreateHiddenWindow() {
96
97
98
99
  if (mHiddenWindow) {
    return NS_OK;
  }

100
101
102
103
  if (!XRE_IsParentProcess()) {
    return NS_ERROR_NOT_IMPLEMENTED;
  }

104
105
106
107
108
109
110
111
112
113
114
115
  if (mXPCOMShuttingDown) {
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<nsIFile> profileDir;
  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                         getter_AddRefs(profileDir));
  if (!profileDir) {
    // This is too early on startup to create the hidden window
    return NS_ERROR_FAILURE;
  }

116
  nsresult rv;
117
  int32_t initialHeight = 100, initialWidth = 100;
118

119
#ifdef XP_MACOSX
120
  uint32_t chromeMask = 0;
121
122
123
  nsAutoCString prefVal;
  rv = Preferences::GetCString("browser.hiddenWindowChromeURL", prefVal);
  const char* hiddenWindowURL =
124
      NS_SUCCEEDED(rv) ? prefVal.get() : DEFAULT_HIDDENWINDOW_URL;
125
  mApplicationProvidedHiddenWindow = prefVal.get() ? true : false;
126
#else
127
  static const char hiddenWindowURL[] = DEFAULT_HIDDENWINDOW_URL;
128
  uint32_t chromeMask = nsIWebBrowserChrome::CHROME_ALL;
129
130
131
132
#endif

  nsCOMPtr<nsIURI> url;
  rv = NS_NewURI(getter_AddRefs(url), hiddenWindowURL);
133
134
  NS_ENSURE_SUCCESS(rv, rv);

135
  RefPtr<AppWindow> newWindow;
136
137
  rv = JustCreateTopWindow(nullptr, url, chromeMask, initialWidth,
                           initialHeight, true, getter_AddRefs(newWindow));
138
  NS_ENSURE_SUCCESS(rv, rv);
139

140
141
142
  nsCOMPtr<nsIDocShell> docShell;
  newWindow->GetDocShell(getter_AddRefs(docShell));
  if (docShell) {
143
    docShell->SetIsActive(false);
144
  }
145

146
  mHiddenWindow.swap(newWindow);
147

148
  return NS_OK;
149
150
}

151
NS_IMETHODIMP
152
nsAppShellService::DestroyHiddenWindow() {
153
  if (mHiddenWindow) {
154
155
    mHiddenWindow->Destroy();

156
    mHiddenWindow = nullptr;
157
158
  }

159
  return NS_OK;
160
161
}

162
163
164
165
/*
 * Create a new top level window and display the given URL within it...
 */
NS_IMETHODIMP
166
167
168
169
170
nsAppShellService::CreateTopLevelWindow(nsIAppWindow* aParent, nsIURI* aUrl,
                                        uint32_t aChromeMask,
                                        int32_t aInitialWidth,
                                        int32_t aInitialHeight,
                                        nsIAppWindow** aResult) {
171
  nsresult rv;
172

173
  StartupTimeline::RecordOnce(StartupTimeline::CREATE_TOP_LEVEL_WINDOW);
174

175
  RefPtr<AppWindow> newWindow;
176
  rv = JustCreateTopWindow(aParent, aUrl, aChromeMask, aInitialWidth,
177
                           aInitialHeight, false, getter_AddRefs(newWindow));
178
  newWindow.forget(aResult);
179

180
  if (NS_SUCCEEDED(rv)) {
181
182
    // the addref resulting from this is the owning addref for this window
    RegisterTopLevelWindow(*aResult);
183
    nsCOMPtr<nsIAppWindow> parent;
184
    if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) parent = aParent;
185
    (*aResult)->SetZLevel(CalculateWindowZLevel(parent, aChromeMask));
186
  }
187
188
189
190

  return rv;
}

191
/*
192
 * This class provides a stub implementation of nsIWebBrowserChrome, as needed
193
194
 * by nsAppShellService::CreateWindowlessBrowser
 */
195
class WebBrowserChrome2Stub final : public nsIWebBrowserChrome,
196
197
198
                                    public nsIEmbeddingSiteWindow,
                                    public nsIInterfaceRequestor,
                                    public nsSupportsWeakReference {
199
200
201
202
203
204
205
206
207
208
209
 protected:
  nsCOMPtr<nsIWebBrowser> mBrowser;
  virtual ~WebBrowserChrome2Stub() {}

 public:
  void SetBrowser(nsIWebBrowser* aBrowser) { mBrowser = aBrowser; }

  NS_DECL_ISUPPORTS
  NS_DECL_NSIWEBBROWSERCHROME
  NS_DECL_NSIINTERFACEREQUESTOR
  NS_DECL_NSIEMBEDDINGSITEWINDOW
210
211
212
213
214
215
};

NS_INTERFACE_MAP_BEGIN(WebBrowserChrome2Stub)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome)
  NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
216
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
217
  NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
218
219
220
221
222
223
NS_INTERFACE_MAP_END

NS_IMPL_ADDREF(WebBrowserChrome2Stub)
NS_IMPL_RELEASE(WebBrowserChrome2Stub)

NS_IMETHODIMP
224
WebBrowserChrome2Stub::GetChromeFlags(uint32_t* aChromeFlags) {
225
226
227
228
229
  *aChromeFlags = 0;
  return NS_OK;
}

NS_IMETHODIMP
230
231
232
233
WebBrowserChrome2Stub::SetChromeFlags(uint32_t aChromeFlags) {
  MOZ_ASSERT_UNREACHABLE(
      "WebBrowserChrome2Stub::SetChromeFlags is "
      "not supported");
234
235
236
237
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
238
WebBrowserChrome2Stub::ShowAsModal() {
239
  MOZ_ASSERT_UNREACHABLE("WebBrowserChrome2Stub::ShowAsModal is not supported");
240
241
242
243
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
244
WebBrowserChrome2Stub::IsWindowModal(bool* aResult) {
245
246
247
248
249
  *aResult = false;
  return NS_OK;
}

NS_IMETHODIMP
250
WebBrowserChrome2Stub::SetLinkStatus(const nsAString& aStatusText) {
251
252
253
254
  return NS_OK;
}

NS_IMETHODIMP
255
256
WebBrowserChrome2Stub::GetInterface(const nsIID& aIID, void** aSink) {
  return QueryInterface(aIID, aSink);
257
258
}

259
260
// nsIEmbeddingSiteWindow impl
NS_IMETHODIMP
261
262
WebBrowserChrome2Stub::GetDimensions(uint32_t flags, int32_t* x, int32_t* y,
                                     int32_t* cx, int32_t* cy) {
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  if (x) {
    *x = 0;
  }

  if (y) {
    *y = 0;
  }

  if (cx) {
    *cx = 0;
  }

  if (cy) {
    *cy = 0;
  }

  return NS_OK;
}

NS_IMETHODIMP
283
284
WebBrowserChrome2Stub::SetDimensions(uint32_t flags, int32_t x, int32_t y,
                                     int32_t cx, int32_t cy) {
285
286
287
288
  nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser);
  NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
  window->SetSize(cx, cy, true);
  return NS_OK;
289
290
291
}

NS_IMETHODIMP
292
WebBrowserChrome2Stub::SetFocus() { return NS_ERROR_NOT_IMPLEMENTED; }
293
294

NS_IMETHODIMP
295
WebBrowserChrome2Stub::GetVisibility(bool* aVisibility) {
296
297
298
  return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
299
WebBrowserChrome2Stub::SetVisibility(bool aVisibility) {
300
301
302
303
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
304
WebBrowserChrome2Stub::GetTitle(nsAString& aTitle) {
305
306
307
  return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
308
WebBrowserChrome2Stub::SetTitle(const nsAString& aTitle) {
309
310
311
312
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
313
WebBrowserChrome2Stub::GetSiteWindow(void** aSiteWindow) {
314
315
316
317
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
318
WebBrowserChrome2Stub::Blur() { return NS_ERROR_NOT_IMPLEMENTED; }
319

320
321
class BrowserDestroyer final : public Runnable {
 public:
322
  BrowserDestroyer(nsIWebBrowser* aBrowser, nsISupports* aContainer)
323
324
325
      : mozilla::Runnable("BrowserDestroyer"),
        mBrowser(aBrowser),
        mContainer(aContainer) {}
326

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
  static nsresult Destroy(nsIWebBrowser* aBrowser) {
    RefPtr<BrowsingContext> bc;
    if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(aBrowser)) {
      bc = docShell->GetBrowsingContext();
    }

    nsCOMPtr<nsIBaseWindow> window(do_QueryInterface(aBrowser));
    nsresult rv = window->Destroy();
    MOZ_ASSERT(bc);
    if (bc) {
      bc->Detach();
    }
    return rv;
  }

342
  NS_IMETHOD
343
  Run() override {
344
    // Explicitly destroy the browser, in case this isn't the last reference.
345
    return Destroy(mBrowser);
346
347
  }

348
 protected:
349
350
  virtual ~BrowserDestroyer() {}

351
 private:
352
353
354
355
  nsCOMPtr<nsIWebBrowser> mBrowser;
  nsCOMPtr<nsISupports> mContainer;
};

356
// This is the "stub" we return from CreateWindowlessBrowser - it exists
357
358
359
360
361
// to manage the lifetimes of the nsIWebBrowser and container window.
// In particular, it keeps a strong reference to both, to prevent them from
// being collected while this object remains alive, and ensures that they
// aren't destroyed when it's not safe to run scripts.
class WindowlessBrowser final : public nsIWindowlessBrowser,
362
363
364
365
                                public nsIInterfaceRequestor {
 public:
  WindowlessBrowser(nsIWebBrowser* aBrowser, nsISupports* aContainer)
      : mBrowser(aBrowser), mContainer(aContainer), mClosed(false) {
366
367
368
369
    mWebNavigation = do_QueryInterface(aBrowser);
    mInterfaceRequestor = do_QueryInterface(aBrowser);
  }
  NS_DECL_ISUPPORTS
370
  NS_DECL_NSIWINDOWLESSBROWSER
371
372
373
  NS_FORWARD_SAFE_NSIWEBNAVIGATION(mWebNavigation)
  NS_FORWARD_SAFE_NSIINTERFACEREQUESTOR(mInterfaceRequestor)

374
375
 private:
  ~WindowlessBrowser() {
376
377
378
379
380
381
382
383
384
385
    if (mClosed) {
      return;
    }

    NS_WARNING("Windowless browser was not closed prior to destruction");

    // The docshell destructor needs to dispatch events, and can only run
    // when it's safe to run scripts. If this was triggered by GC, it may
    // not always be safe to run scripts, in which cases we need to delay
    // destruction until it is.
386
387
    auto runnable = MakeRefPtr<BrowserDestroyer>(mBrowser, mContainer);
    nsContentUtils::AddScriptRunner(runnable.forget());
388
389
  }

390
391
392
393
394
395
  nsCOMPtr<nsIWebBrowser> mBrowser;
  nsCOMPtr<nsIWebNavigation> mWebNavigation;
  nsCOMPtr<nsIInterfaceRequestor> mInterfaceRequestor;
  // we don't use the container but just hold a reference to it.
  nsCOMPtr<nsISupports> mContainer;

396
397
  bool mClosed;
};
398

399
400
NS_IMPL_ISUPPORTS(WindowlessBrowser, nsIWindowlessBrowser, nsIWebNavigation,
                  nsIInterfaceRequestor)
401

402
NS_IMETHODIMP
403
WindowlessBrowser::Close() {
404
405
406
407
408
409
410
411
  NS_ENSURE_TRUE(!mClosed, NS_ERROR_UNEXPECTED);
  NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
               "WindowlessBrowser::Close called when not safe to run scripts");

  mClosed = true;

  mWebNavigation = nullptr;
  mInterfaceRequestor = nullptr;
412
  return BrowserDestroyer::Destroy(mBrowser);
413
414
}

415
416
417
418
419
420
421
422
423
NS_IMETHODIMP
WindowlessBrowser::GetBrowsingContext(BrowsingContext** aBrowsingContext) {
  nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = do_QueryInterface(mBrowser);
  if (!docShellTreeItem) {
    return NS_ERROR_NOT_INITIALIZED;
  }
  return docShellTreeItem->GetBrowsingContextXPCOM(aBrowsingContext);
}

424
NS_IMETHODIMP
425
WindowlessBrowser::GetDocShell(nsIDocShell** aDocShell) {
426
427
428
429
430
431
432
433
  nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mInterfaceRequestor);
  if (!docShell) {
    return NS_ERROR_NOT_INITIALIZED;
  }
  docShell.forget(aDocShell);
  return NS_OK;
}

434
NS_IMETHODIMP
435
436
nsAppShellService::CreateWindowlessBrowser(bool aIsChrome,
                                           nsIWindowlessBrowser** aResult) {
437
  /* First, we set the container window for our instance of nsWebBrowser. Since
438
439
   * we don't actually have a window, we instead set the container window to be
   * an instance of WebBrowserChrome2Stub, which provides a stub implementation
440
   * of nsIWebBrowserChrome.
441
   */
442
  RefPtr<WebBrowserChrome2Stub> stub = new WebBrowserChrome2Stub();
443
444
445

  /* A windowless web browser doesn't have an associated OS level window. To
   * accomplish this, we initialize the window associated with our instance of
446
447
   * nsWebBrowser with an instance of HeadlessWidget/PuppetWidget, which provide
   * a stub implementation of nsIWidget.
448
   */
449
450
451
452
453
454
  nsCOMPtr<nsIWidget> widget;
  if (gfxPlatform::IsHeadless()) {
    widget = nsIWidget::CreateHeadlessWidget();
  } else {
    widget = nsIWidget::CreatePuppetWidget(nullptr);
  }
455
  if (!widget) {
456
    NS_ERROR("Couldn't create instance of stub widget");
457
458
    return NS_ERROR_FAILURE;
  }
459

460
  nsresult rv =
461
      widget->Create(nullptr, 0, LayoutDeviceIntRect(0, 0, 0, 0), nullptr);
462
  NS_ENSURE_SUCCESS(rv, rv);
463

464
  // Create a BrowsingContext for our windowless browser.
465
  RefPtr<BrowsingContext> browsingContext = BrowsingContext::CreateIndependent(
466
467
      aIsChrome ? BrowsingContext::Type::Chrome
                : BrowsingContext::Type::Content);
468

469
470
471
  /* Next, we create an instance of nsWebBrowser. Instances of this class have
   * an associated doc shell, which is what we're interested in.
   */
472
  nsCOMPtr<nsIWebBrowser> browser = nsWebBrowser::Create(
473
      stub, widget, browsingContext, nullptr /* initialWindowChild */);
474
475
476
477
478
479
480
481

  if (NS_WARN_IF(!browser)) {
    NS_ERROR("Couldn't create instance of nsWebBrowser!");
    return NS_ERROR_FAILURE;
  }

  // Make sure the container window owns the the nsWebBrowser instance.
  stub->SetBrowser(browser);
482

483
  nsISupports* isstub = NS_ISUPPORTS_CAST(nsIWebBrowserChrome*, stub);
484
  RefPtr<nsIWindowlessBrowser> result = new WindowlessBrowser(browser, isstub);
485
486
487
  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(result);
  docshell->SetInvisible(true);

488
  result.forget(aResult);
489
490
491
  return NS_OK;
}

492
uint32_t nsAppShellService::CalculateWindowZLevel(nsIAppWindow* aParent,
493
                                                  uint32_t aChromeMask) {
494
  uint32_t zLevel;
495

496
  zLevel = nsIAppWindow::normalZ;
497
  if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RAISED)
498
    zLevel = nsIAppWindow::raisedZ;
499
  else if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_LOWERED)
500
    zLevel = nsIAppWindow::loweredZ;
501

502
#ifdef XP_MACOSX
503
504
505
506
507
508
  /* Platforms on which modal windows are always application-modal, not
     window-modal (that's just the Mac, right?) want modal windows to
     be stacked on top of everyone else.

     On Mac OS X, bind modality to parent window instead of app (ala Mac OS 9)
  */
509
510
  uint32_t modalDepMask =
      nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT;
511
  if (aParent && (aChromeMask & modalDepMask)) {
512
    aParent->GetZLevel(&zLevel);
513
514
515
516
517
518
519
520
521
522
523
524
  }
#else
  /* Platforms with native support for dependent windows (that's everyone
      but pre-Mac OS X, right?) know how to stack dependent windows. On these
      platforms, give the dependent window the same level as its parent,
      so we won't try to override the normal platform behaviour. */
  if ((aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) && aParent)
    aParent->GetZLevel(&zLevel);
#endif

  return zLevel;
}
525

526
#ifdef XP_WIN
527
528
529
/*
 * Checks to see if any existing window is currently in fullscreen mode.
 */
530
static bool CheckForFullscreenWindow() {
531
  nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
532
  if (!wm) return false;
533
534

  nsCOMPtr<nsISimpleEnumerator> windowList;
535
  wm->GetAppWindowEnumerator(nullptr, getter_AddRefs(windowList));
536
  if (!windowList) return false;
537
538

  for (;;) {
539
    bool more = false;
540
    windowList->HasMoreElements(&more);
541
    if (!more) return false;
542
543
544
545
546
547
548

    nsCOMPtr<nsISupports> supportsWindow;
    windowList->GetNext(getter_AddRefs(supportsWindow));
    nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow));
    if (baseWin) {
      nsCOMPtr<nsIWidget> widget;
      baseWin->GetMainWidget(getter_AddRefs(widget));
549
      if (widget && widget->SizeMode() == nsSizeMode_Fullscreen) {
550
        return true;
551
552
553
      }
    }
  }
554
  return false;
555
}
556
#endif
557

558
559
560
/*
 * Just do the window-making part of CreateTopLevelWindow
 */
561
nsresult nsAppShellService::JustCreateTopWindow(
562
    nsIAppWindow* aParent, nsIURI* aUrl, uint32_t aChromeMask,
563
    int32_t aInitialWidth, int32_t aInitialHeight, bool aIsHiddenWindow,
564
    AppWindow** aResult) {
565
  *aResult = nullptr;
566
  NS_ENSURE_STATE(!mXPCOMWillShutDown);
567

568
  nsCOMPtr<nsIAppWindow> parent;
569
  if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) parent = aParent;
570

571
  RefPtr<AppWindow> window = new AppWindow(aChromeMask);
572

573
574
575
576
#ifdef XP_WIN
  // If the parent is currently fullscreen, tell the child to ignore persisted
  // full screen states. This way new browser windows open on top of fullscreen
  // windows normally.
577
  if (window && CheckForFullscreenWindow()) window->IgnoreXULSizeMode(true);
578
579
#endif

580
581
582
583
584
  nsWidgetInitData widgetInitData;

  if (aIsHiddenWindow)
    widgetInitData.mWindowType = eWindowType_invisible;
  else
585
586
587
588
    widgetInitData.mWindowType =
        aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG
            ? eWindowType_dialog
            : eWindowType_toplevel;
589
590
591

  if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)
    widgetInitData.mWindowType = eWindowType_popup;
592

593
  if (aChromeMask & nsIWebBrowserChrome::CHROME_SUPPRESS_ANIMATION)
594
595
    widgetInitData.mIsAnimationSuppressed = true;

596
597
598
  if (aChromeMask & nsIWebBrowserChrome::CHROME_ALWAYS_ON_TOP)
    widgetInitData.mAlwaysOnTop = true;

599
600
601
  if (aChromeMask & nsIWebBrowserChrome::CHROME_FISSION_WINDOW)
    widgetInitData.mFissionWindow = true;

602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
#ifdef MOZ_WIDGET_GTK
  // Linux/Gtk PIP window support. It's Chrome Toplevel window, always on top
  // and without any bar.
  uint32_t pipMask = nsIWebBrowserChrome::CHROME_ALWAYS_ON_TOP |
                     nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
  uint32_t barMask = nsIWebBrowserChrome::CHROME_MENUBAR |
                     nsIWebBrowserChrome::CHROME_TOOLBAR |
                     nsIWebBrowserChrome::CHROME_LOCATIONBAR |
                     nsIWebBrowserChrome::CHROME_STATUSBAR;
  if (widgetInitData.mWindowType == eWindowType_toplevel &&
      ((aChromeMask & pipMask) == pipMask) && !(aChromeMask & barMask)) {
    widgetInitData.mPIPWindow = true;
  }
#endif

617
#ifdef XP_MACOSX
618
  // Mac OS X sheet support
619
620
621
622
623
  // Adding CHROME_OPENAS_CHROME to sheetMask makes modal windows opened from
  // nsGlobalWindow::ShowModalDialog() be dialogs (not sheets), while modal
  // windows opened from nsPromptService::DoDialog() still are sheets.  This
  // fixes bmo bug 395465 (see nsCocoaWindow::StandardCreate() and
  // nsCocoaWindow::SetModal()).
624
  uint32_t sheetMask = nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
625
626
                       nsIWebBrowserChrome::CHROME_MODAL |
                       nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
627
  if (parent && (parent != mHiddenWindow) &&
628
      ((aChromeMask & sheetMask) == sheetMask)) {
629
    widgetInitData.mWindowType = eWindowType_sheet;
630
  }
631
632
#endif

633
634
635
#if defined(XP_WIN)
  if (widgetInitData.mWindowType == eWindowType_toplevel ||
      widgetInitData.mWindowType == eWindowType_dialog)
636
    widgetInitData.clipChildren = true;
637
638
#endif

639
640
641
642
  // note default chrome overrides other OS chrome settings, but
  // not internal chrome
  if (aChromeMask & nsIWebBrowserChrome::CHROME_DEFAULT)
    widgetInitData.mBorderStyle = eBorderStyle_default;
643
644
  else if ((aChromeMask & nsIWebBrowserChrome::CHROME_ALL) ==
           nsIWebBrowserChrome::CHROME_ALL)
645
646
    widgetInitData.mBorderStyle = eBorderStyle_all;
  else {
647
    widgetInitData.mBorderStyle = eBorderStyle_none;  // assumes none == 0x00
648
    if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_BORDERS)
649
650
      widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
          widgetInitData.mBorderStyle | eBorderStyle_border);
651
    if (aChromeMask & nsIWebBrowserChrome::CHROME_TITLEBAR)
652
653
      widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
          widgetInitData.mBorderStyle | eBorderStyle_title);
654
    if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_CLOSE)
655
656
      widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
          widgetInitData.mBorderStyle | eBorderStyle_close);
657
    if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
658
      widgetInitData.mResizable = true;
659
660
      widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
          widgetInitData.mBorderStyle | eBorderStyle_resizeh);
661
      // only resizable windows get the maximize button (but not dialogs)
662
      if (!(aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG))
663
664
        widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
            widgetInitData.mBorderStyle | eBorderStyle_maximize);
665
    }
666
667
    // all windows (except dialogs) get minimize buttons and the system menu
    if (!(aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG))
668
669
670
      widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
          widgetInitData.mBorderStyle | eBorderStyle_minimize |
          eBorderStyle_menu);
671
672
    // but anyone can explicitly ask for a minimize button
    if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_MIN) {
673
674
      widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(
          widgetInitData.mBorderStyle | eBorderStyle_minimize);
675
    }
676
  }
677

678
679
680
681
  if (aInitialWidth == nsIAppShellService::SIZE_TO_CONTENT ||
      aInitialHeight == nsIAppShellService::SIZE_TO_CONTENT) {
    aInitialWidth = 1;
    aInitialHeight = 1;
682
    window->SetIntrinsicallySized(true);
683
  }
684

685
  bool center = aChromeMask & nsIWebBrowserChrome::CHROME_CENTER_SCREEN;
686

687
  widgetInitData.mRTL = LocaleService::GetInstance()->IsAppLocaleRTL();
688

689
690
691
  nsresult rv =
      window->Initialize(parent, center ? aParent : nullptr, aInitialWidth,
                         aInitialHeight, aIsHiddenWindow, widgetInitData);
692
693

  NS_ENSURE_SUCCESS(rv, rv);
694

695
696
  // Enforce the Private Browsing autoStart pref first.
  bool isPrivateBrowsingWindow =
697
      Preferences::GetBool("browser.privatebrowsing.autostart");
698

699
700
701
702
  if (aChromeMask & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) {
    // Caller requested a private window
    isPrivateBrowsingWindow = true;
  }
703
  nsCOMPtr<mozIDOMWindowProxy> domWin = do_GetInterface(aParent);
704
705
  nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(domWin);
  nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(webNav);
706

707
  if (!isPrivateBrowsingWindow && parentContext) {
708
709
710
    // Ensure that we propagate any existing private browsing status
    // from the parent, even if it will not actually be used
    // as a parent value.
711
    isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
712
713
  }

714
  if (nsDocShell* docShell = nsDocShell::Cast(window->GetDocShell())) {
715
    MOZ_ASSERT(docShell->GetBrowsingContext()->IsChrome());
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746

    docShell->SetPrivateBrowsing(isPrivateBrowsingWindow);
    docShell->SetRemoteTabs(aChromeMask &
                            nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
    docShell->SetRemoteSubframes(aChromeMask &
                                 nsIWebBrowserChrome::CHROME_FISSION_WINDOW);

    // Eagerly create an about:blank content viewer with the right principal
    // here, rather than letting it happening in the upcoming call to
    // SetInitialPrincipalToSubject. This avoids creating the about:blank
    // document and then blowing it away with a second one, which can cause
    // problems for the top-level chrome window case. See bug 789773. Note that
    // we don't accept expanded principals here, similar to
    // SetInitialPrincipalToSubject.
    if (nsContentUtils::IsInitialized()) {  // Sometimes this happens really
                                            // early. See bug 793370.
      nsCOMPtr<nsIPrincipal> principal =
          nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
      if (nsContentUtils::IsExpandedPrincipal(principal)) {
        principal = nullptr;
      }
      // Use the subject (or system) principal as the storage principal too
      // until the new window finishes navigating and gets a real storage
      // principal.
      rv = docShell->CreateAboutBlankContentViewer(principal, principal,
                                                   /* aCsp = */ nullptr);
      NS_ENSURE_SUCCESS(rv, rv);
      RefPtr<Document> doc = docShell->GetDocument();
      NS_ENSURE_TRUE(!!doc, NS_ERROR_FAILURE);
      doc->SetIsInitialDocument(true);
    }
747

748
749
750
751
752
753
754
755
    // Begin loading the URL provided.
    if (aUrl) {
      RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aUrl);
      loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
      loadState->SetFirstParty(true);
      rv = docShell->LoadURI(loadState, /* aSetNavigating */ true);
      NS_ENSURE_SUCCESS(rv, rv);
    }
756
757
  }

758
  window.forget(aResult);
759
  if (parent) parent->AddChildWindow(*aResult);
760

761
  if (center) rv = (*aResult)->Center(parent, parent ? false : true, false);
762
763
764
765

  return rv;
}

766
NS_IMETHODIMP
767
nsAppShellService::GetHiddenWindow(nsIAppWindow** aWindow) {
768
  NS_ENSURE_ARG_POINTER(aWindow);
769

770
771
  EnsureHiddenWindow();

772
773
774
  *aWindow = mHiddenWindow;
  NS_IF_ADDREF(*aWindow);
  return *aWindow ? NS_OK : NS_ERROR_FAILURE;
775
776
}

777
NS_IMETHODIMP
778
nsAppShellService::GetHiddenDOMWindow(mozIDOMWindowProxy** aWindow) {
779
780
781
782
  NS_ENSURE_ARG_POINTER(aWindow);

  EnsureHiddenWindow();

783
784
  nsresult rv;
  nsCOMPtr<nsIDocShell> docShell;
785
786
  NS_ENSURE_TRUE(mHiddenWindow, NS_ERROR_FAILURE);

787
  rv = mHiddenWindow->GetDocShell(getter_AddRefs(docShell));
788
  NS_ENSURE_SUCCESS(rv, rv);
789
  NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
790

791
  nsCOMPtr<nsPIDOMWindowOuter> hiddenDOMWindow(docShell->GetWindow());
792
793
  hiddenDOMWindow.forget(aWindow);
  return *aWindow ? NS_OK : NS_ERROR_FAILURE;
794
795
}

796
797
798
799
800
801
802
803
NS_IMETHODIMP
nsAppShellService::GetHasHiddenWindow(bool* aHasHiddenWindow) {
  NS_ENSURE_ARG_POINTER(aHasHiddenWindow);

  *aHasHiddenWindow = !!mHiddenWindow;
  return NS_OK;
}

804
NS_IMETHODIMP
805
806
807
nsAppShellService::GetApplicationProvidedHiddenWindow(bool* aAPHW) {
  *aAPHW = mApplicationProvidedHiddenWindow;
  return NS_OK;
808
809
}

810
811
812
/*
 * Register a new top level window (created elsewhere)
 */
813
NS_IMETHODIMP
814
nsAppShellService::RegisterTopLevelWindow(nsIAppWindow* aWindow) {
815
816
  NS_ENSURE_ARG_POINTER(aWindow);

817
818
  nsCOMPtr<nsIDocShell> docShell;
  aWindow->GetDocShell(getter_AddRefs(docShell));
819
820
  NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);

821
  nsCOMPtr<nsPIDOMWindowOuter> domWindow(docShell->GetWindow());
822
  NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
823
  domWindow->SetInitialPrincipalToSubject(nullptr);
824

825
  // tell the window mediator about the new window
826
827
  nsCOMPtr<nsIWindowMediator> mediator(
      do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
828
829
  NS_ASSERTION(mediator, "Couldn't get window mediator.");

830
  if (mediator) mediator->RegisterWindow(aWindow);
831
832

  // tell the window watcher about the new window
833
834
  nsCOMPtr<nsPIWindowWatcher> wwatcher(
      do_GetService(NS_WINDOWWATCHER_CONTRACTID));
835
  NS_ASSERTION(wwatcher, "No windowwatcher?");
836
837
  if (wwatcher && domWindow) {
    wwatcher->AddWindow(domWindow, 0);
838
  }
839

840
  // an ongoing attempt to quit is stopped by a newly opened window
841
  nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService();
842
843
  NS_ASSERTION(obssvc, "Couldn't get observer service.");

844
  if (obssvc) {
845
    obssvc->NotifyObservers(aWindow, "xul-window-registered", nullptr);
846
847
    AppWindow* appWindow = static_cast<AppWindow*>(aWindow);
    appWindow->WasRegistered();
848
  }
849

850
  return NS_OK;
851
}
852

853
NS_IMETHODIMP
854
nsAppShellService::UnregisterTopLevelWindow(nsIAppWindow* aWindow) {
855
  if (mXPCOMShuttingDown) {
856
857
858
859
860
861
    /* return an error code in order to:
       - avoid doing anything with other member variables while we are in
         the destructor
       - notify the caller not to release the AppShellService after
         unregistering the window
         (we don't want to be deleted twice consecutively to
862
         mHiddenWindow->Destroy() in our destructor)
863
864
865
    */
    return NS_ERROR_FAILURE;
  }
866

867
868
  NS_ENSURE_ARG_POINTER(aWindow);

869
870
871
872
873
  if (aWindow == mHiddenWindow) {
    // CreateHiddenWindow() does not register the window, so we're done.
    return NS_OK;
  }

874
  // tell the window mediator
875
876
  nsCOMPtr<nsIWindowMediator> mediator(
      do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
877