Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/windowwatcher/nsWindowWatcher.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsWindowWatcher.h"
8
#include "nsAutoWindowStateHelper.h"
9
10
#include "nsCRT.h"
11
#include "nsNetUtil.h"
12
#include "nsIAuthPrompt.h"
13
#include "nsIAuthPrompt2.h"
14
#include "nsISimpleEnumerator.h"
15
#include "nsIInterfaceRequestorUtils.h"
16
#include "nsJSUtils.h"
17
#include "plstr.h"
18
19
#include "nsDocShell.h"
20
#include "nsGlobalWindow.h"
21
#include "nsHashPropertyBag.h"
22
#include "nsIBaseWindow.h"
23
#include "nsIBrowserDOMWindow.h"
24
#include "nsIDocShell.h"
25
#include "nsDocShellLoadInfo.h"
26
#include "nsIDocShellTreeItem.h"
27
#include "nsIDocShellTreeOwner.h"
28
#include "nsIDocumentLoader.h"
29
#include "nsIDocument.h"
30
#include "nsIDOMWindow.h"
31
#include "nsIDOMChromeWindow.h"
32
#include "nsIPrompt.h"
33
#include "nsIScriptObjectPrincipal.h"
34
#include "nsIScreen.h"
35
#include "nsIScreenManager.h"
36
#include "nsIScriptContext.h"
37
#include "nsIObserverService.h"
38
#include "nsIScriptSecurityManager.h"
39
#include "nsXPCOM.h"
40
#include "nsIURI.h"
41
#include "nsIWebBrowser.h"
42
#include "nsIWebBrowserChrome.h"
43
#include "nsIWebNavigation.h"
44
#include "nsIWindowCreator.h"
45
#include "nsIWindowCreator2.h"
46
#include "nsIXPConnect.h"
47
#include "nsIXULRuntime.h"
48
#include "nsPIDOMWindow.h"
49
#include "nsIContentViewer.h"
50
#include "nsIWindowProvider.h"
51
#include "nsIMutableArray.h"
52
#include "nsIDOMStorageManager.h"
53
#include "nsIWidget.h"
54
#include "nsFocusManager.h"
55
#include "nsIPresShell.h"
56
#include "nsPresContext.h"
57
#include "nsContentUtils.h"
58
#include "nsIPrefBranch.h"
59
#include "nsIPrefService.h"
60
#include "nsSandboxFlags.h"
61
#include "nsSimpleEnumerator.h"
62
#include "mozilla/CheckedInt.h"
63
#include "mozilla/Preferences.h"
64
#include "mozilla/dom/Element.h"
65
#include "mozilla/dom/Storage.h"
66
#include "mozilla/dom/ScriptSettings.h"
67
#include "mozilla/dom/TabParent.h"
68
#include "mozilla/dom/DocGroup.h"
69
#include "mozilla/dom/TabGroup.h"
70
#include "nsIXULWindow.h"
71
#include "nsIXULBrowserWindow.h"
72
#include "nsGlobalWindow.h"
73
74
using namespace mozilla;
75
using namespace mozilla::dom;
76
77
/****************************************************************
78
 ******************** nsWatcherWindowEntry **********************
79
 ****************************************************************/
80
81
class nsWindowWatcher;
82
83
struct nsWatcherWindowEntry
84
{
85
86
  nsWatcherWindowEntry(mozIDOMWindowProxy* aWindow, nsIWebBrowserChrome* aChrome)
87
    : mChrome(nullptr)
88
0
  {
89
0
    mWindow = aWindow;
90
0
    nsCOMPtr<nsISupportsWeakReference> supportsweak(do_QueryInterface(aChrome));
91
0
    if (supportsweak) {
92
0
      supportsweak->GetWeakReference(getter_AddRefs(mChromeWeak));
93
0
    } else {
94
0
      mChrome = aChrome;
95
0
      mChromeWeak = nullptr;
96
0
    }
97
0
    ReferenceSelf();
98
0
  }
99
0
  ~nsWatcherWindowEntry() {}
100
101
  void InsertAfter(nsWatcherWindowEntry* aOlder);
102
  void Unlink();
103
  void ReferenceSelf();
104
105
  mozIDOMWindowProxy* mWindow;
106
  nsIWebBrowserChrome* mChrome;
107
  nsWeakPtr mChromeWeak;
108
  // each struct is in a circular, doubly-linked list
109
  nsWatcherWindowEntry* mYounger; // next younger in sequence
110
  nsWatcherWindowEntry* mOlder;
111
};
112
113
void
114
nsWatcherWindowEntry::InsertAfter(nsWatcherWindowEntry* aOlder)
115
0
{
116
0
  if (aOlder) {
117
0
    mOlder = aOlder;
118
0
    mYounger = aOlder->mYounger;
119
0
    mOlder->mYounger = this;
120
0
    if (mOlder->mOlder == mOlder) {
121
0
      mOlder->mOlder = this;
122
0
    }
123
0
    mYounger->mOlder = this;
124
0
    if (mYounger->mYounger == mYounger) {
125
0
      mYounger->mYounger = this;
126
0
    }
127
0
  }
128
0
}
129
130
void
131
nsWatcherWindowEntry::Unlink()
132
0
{
133
0
  mOlder->mYounger = mYounger;
134
0
  mYounger->mOlder = mOlder;
135
0
  ReferenceSelf();
136
0
}
137
138
void
139
nsWatcherWindowEntry::ReferenceSelf()
140
0
{
141
0
142
0
  mYounger = this;
143
0
  mOlder = this;
144
0
}
145
146
/****************************************************************
147
 ****************** nsWatcherWindowEnumerator *******************
148
 ****************************************************************/
149
150
class nsWatcherWindowEnumerator : public nsSimpleEnumerator
151
{
152
153
public:
154
  explicit nsWatcherWindowEnumerator(nsWindowWatcher* aWatcher);
155
  NS_IMETHOD HasMoreElements(bool* aResult) override;
156
  NS_IMETHOD GetNext(nsISupports** aResult) override;
157
158
protected:
159
  ~nsWatcherWindowEnumerator() override;
160
161
private:
162
  friend class nsWindowWatcher;
163
164
  nsWatcherWindowEntry* FindNext();
165
  void WindowRemoved(nsWatcherWindowEntry* aInfo);
166
167
  nsWindowWatcher* mWindowWatcher;
168
  nsWatcherWindowEntry* mCurrentPosition;
169
};
170
171
nsWatcherWindowEnumerator::nsWatcherWindowEnumerator(nsWindowWatcher* aWatcher)
172
  : mWindowWatcher(aWatcher)
173
  , mCurrentPosition(aWatcher->mOldestWindow)
174
0
{
175
0
  mWindowWatcher->AddEnumerator(this);
176
0
  mWindowWatcher->AddRef();
177
0
}
178
179
nsWatcherWindowEnumerator::~nsWatcherWindowEnumerator()
180
0
{
181
0
  mWindowWatcher->RemoveEnumerator(this);
182
0
  mWindowWatcher->Release();
183
0
}
184
185
NS_IMETHODIMP
186
nsWatcherWindowEnumerator::HasMoreElements(bool* aResult)
187
0
{
188
0
  if (!aResult) {
189
0
    return NS_ERROR_INVALID_ARG;
190
0
  }
191
0
192
0
  *aResult = !!mCurrentPosition;
193
0
  return NS_OK;
194
0
}
195
196
NS_IMETHODIMP
197
nsWatcherWindowEnumerator::GetNext(nsISupports** aResult)
198
0
{
199
0
  if (!aResult) {
200
0
    return NS_ERROR_INVALID_ARG;
201
0
  }
202
0
203
0
  *aResult = nullptr;
204
0
205
0
  if (mCurrentPosition) {
206
0
    CallQueryInterface(mCurrentPosition->mWindow, aResult);
207
0
    mCurrentPosition = FindNext();
208
0
    return NS_OK;
209
0
  }
210
0
  return NS_ERROR_FAILURE;
211
0
}
212
213
nsWatcherWindowEntry*
214
nsWatcherWindowEnumerator::FindNext()
215
0
{
216
0
  nsWatcherWindowEntry* info;
217
0
218
0
  if (!mCurrentPosition) {
219
0
    return 0;
220
0
  }
221
0
222
0
  info = mCurrentPosition->mYounger;
223
0
  return info == mWindowWatcher->mOldestWindow ? 0 : info;
224
0
}
225
226
// if a window is being removed adjust the iterator's current position
227
void
228
nsWatcherWindowEnumerator::WindowRemoved(nsWatcherWindowEntry* aInfo)
229
0
{
230
0
231
0
  if (mCurrentPosition == aInfo) {
232
0
    mCurrentPosition =
233
0
      mCurrentPosition != aInfo->mYounger ? aInfo->mYounger : 0;
234
0
  }
235
0
}
236
237
/****************************************************************
238
 *********************** nsWindowWatcher ************************
239
 ****************************************************************/
240
241
NS_IMPL_ADDREF(nsWindowWatcher)
242
NS_IMPL_RELEASE(nsWindowWatcher)
243
NS_IMPL_QUERY_INTERFACE(nsWindowWatcher,
244
                        nsIWindowWatcher,
245
                        nsIPromptFactory,
246
                        nsPIWindowWatcher)
247
248
nsWindowWatcher::nsWindowWatcher()
249
  : mEnumeratorList()
250
  , mOldestWindow(0)
251
  , mListLock("nsWindowWatcher.mListLock")
252
0
{
253
0
}
254
255
nsWindowWatcher::~nsWindowWatcher()
256
0
{
257
0
  // delete data
258
0
  while (mOldestWindow) {
259
0
    RemoveWindow(mOldestWindow);
260
0
  }
261
0
}
262
263
nsresult
264
nsWindowWatcher::Init()
265
0
{
266
0
  return NS_OK;
267
0
}
268
269
/**
270
 * Convert aArguments into either an nsIArray or nullptr.
271
 *
272
 *  - If aArguments is nullptr, return nullptr.
273
 *  - If aArguments is an nsArray, return nullptr if it's empty, or otherwise
274
 *    return the array.
275
 *  - If aArguments is an nsIArray, return nullptr if it's empty, or
276
 *    otherwise just return the array.
277
 *  - Otherwise, return an nsIArray with one element: aArguments.
278
 */
279
static already_AddRefed<nsIArray>
280
ConvertArgsToArray(nsISupports* aArguments)
281
0
{
282
0
  if (!aArguments) {
283
0
    return nullptr;
284
0
  }
285
0
286
0
  nsCOMPtr<nsIArray> array = do_QueryInterface(aArguments);
287
0
  if (array) {
288
0
    uint32_t argc = 0;
289
0
    array->GetLength(&argc);
290
0
    if (argc == 0) {
291
0
      return nullptr;
292
0
    }
293
0
294
0
    return array.forget();
295
0
  }
296
0
297
0
  nsCOMPtr<nsIMutableArray> singletonArray =
298
0
    do_CreateInstance(NS_ARRAY_CONTRACTID);
299
0
  NS_ENSURE_TRUE(singletonArray, nullptr);
300
0
301
0
  nsresult rv = singletonArray->AppendElement(aArguments);
302
0
  NS_ENSURE_SUCCESS(rv, nullptr);
303
0
304
0
  return singletonArray.forget();
305
0
}
306
307
NS_IMETHODIMP
308
nsWindowWatcher::OpenWindow(mozIDOMWindowProxy* aParent,
309
                            const char* aUrl,
310
                            const char* aName,
311
                            const char* aFeatures,
312
                            nsISupports* aArguments,
313
                            mozIDOMWindowProxy** aResult)
314
0
{
315
0
  nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
316
0
317
0
  uint32_t argc = 0;
318
0
  if (argv) {
319
0
    argv->GetLength(&argc);
320
0
  }
321
0
  bool dialog = (argc != 0);
322
0
323
0
  return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
324
0
                            /* calledFromJS = */ false, dialog,
325
0
                            /* navigate = */ true, argv,
326
0
                            /* aIsPopupSpam = */ false,
327
0
                            /* aForceNoOpener = */ false,
328
0
                            /* aLoadInfo = */ nullptr,
329
0
                            aResult);
330
0
}
331
332
struct SizeSpec
333
{
334
  SizeSpec()
335
    : mLeft(0)
336
    , mTop(0)
337
    , mOuterWidth(0)
338
    , mOuterHeight(0)
339
    , mInnerWidth(0)
340
    , mInnerHeight(0)
341
    , mLeftSpecified(false)
342
    , mTopSpecified(false)
343
    , mOuterWidthSpecified(false)
344
    , mOuterHeightSpecified(false)
345
    , mInnerWidthSpecified(false)
346
    , mInnerHeightSpecified(false)
347
    , mUseDefaultWidth(false)
348
    , mUseDefaultHeight(false)
349
0
  {
350
0
  }
351
352
  int32_t mLeft;
353
  int32_t mTop;
354
  int32_t mOuterWidth;  // Total window width
355
  int32_t mOuterHeight; // Total window height
356
  int32_t mInnerWidth;  // Content area width
357
  int32_t mInnerHeight; // Content area height
358
359
  bool mLeftSpecified;
360
  bool mTopSpecified;
361
  bool mOuterWidthSpecified;
362
  bool mOuterHeightSpecified;
363
  bool mInnerWidthSpecified;
364
  bool mInnerHeightSpecified;
365
366
  // If these booleans are true, don't look at the corresponding width values
367
  // even if they're specified -- they'll be bogus
368
  bool mUseDefaultWidth;
369
  bool mUseDefaultHeight;
370
371
  bool PositionSpecified() const
372
0
  {
373
0
    return mLeftSpecified || mTopSpecified;
374
0
  }
375
376
  bool SizeSpecified() const
377
0
  {
378
0
    return mOuterWidthSpecified || mOuterHeightSpecified ||
379
0
           mInnerWidthSpecified || mInnerHeightSpecified;
380
0
  }
381
};
382
383
NS_IMETHODIMP
384
nsWindowWatcher::OpenWindow2(mozIDOMWindowProxy* aParent,
385
                             const char* aUrl,
386
                             const char* aName,
387
                             const char* aFeatures,
388
                             bool aCalledFromScript,
389
                             bool aDialog,
390
                             bool aNavigate,
391
                             nsISupports* aArguments,
392
                             bool aIsPopupSpam,
393
                             bool aForceNoOpener,
394
                             nsDocShellLoadInfo* aLoadInfo,
395
                             mozIDOMWindowProxy** aResult)
396
0
{
397
0
  nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
398
0
399
0
  uint32_t argc = 0;
400
0
  if (argv) {
401
0
    argv->GetLength(&argc);
402
0
  }
403
0
404
0
  // This is extremely messed up, but this behavior is necessary because
405
0
  // callers lie about whether they're a dialog window and whether they're
406
0
  // called from script.  Fixing this is bug 779939.
407
0
  bool dialog = aDialog;
408
0
  if (!aCalledFromScript) {
409
0
    dialog = argc > 0;
410
0
  }
411
0
412
0
  return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
413
0
                            aCalledFromScript, dialog,
414
0
                            aNavigate, argv, aIsPopupSpam,
415
0
                            aForceNoOpener, aLoadInfo, aResult);
416
0
}
417
418
// This static function checks if the aDocShell uses an UserContextId equal to
419
// the userContextId of subjectPrincipal, if not null.
420
static bool
421
CheckUserContextCompatibility(nsIDocShell* aDocShell)
422
0
{
423
0
  MOZ_ASSERT(aDocShell);
424
0
425
0
  uint32_t userContextId =
426
0
    static_cast<nsDocShell*>(aDocShell)->GetOriginAttributes().mUserContextId;
427
0
428
0
  nsCOMPtr<nsIPrincipal> subjectPrincipal =
429
0
    nsContentUtils::GetCurrentJSContext()
430
0
      ? nsContentUtils::SubjectPrincipal() : nullptr;
431
0
432
0
  // If we don't have a valid principal, probably we are in e10s mode, parent
433
0
  // side.
434
0
  if (!subjectPrincipal) {
435
0
    return true;
436
0
  }
437
0
438
0
  // DocShell can have UsercontextID set but loading a document with system
439
0
  // principal. In this case, we consider everything ok.
440
0
  if (nsContentUtils::IsSystemPrincipal(subjectPrincipal)) {
441
0
    return true;
442
0
  }
443
0
444
0
  return subjectPrincipal->GetUserContextId() == userContextId;
445
0
}
446
447
nsresult
448
nsWindowWatcher::CreateChromeWindow(const nsACString& aFeatures,
449
                                    nsIWebBrowserChrome* aParentChrome,
450
                                    uint32_t aChromeFlags,
451
                                    nsITabParent* aOpeningTabParent,
452
                                    mozIDOMWindowProxy* aOpener,
453
                                    uint64_t aNextTabParentId,
454
                                    nsIWebBrowserChrome** aResult)
455
0
{
456
0
  nsCOMPtr<nsIWindowCreator2> windowCreator2(do_QueryInterface(mWindowCreator));
457
0
  if (NS_WARN_IF(!windowCreator2)) {
458
0
    return NS_ERROR_UNEXPECTED;
459
0
  }
460
0
461
0
  bool cancel = false;
462
0
  nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
463
0
  nsresult rv =
464
0
    windowCreator2->CreateChromeWindow2(aParentChrome, aChromeFlags,
465
0
                                        aOpeningTabParent, aOpener,
466
0
                                        aNextTabParentId, &cancel,
467
0
                                        getter_AddRefs(newWindowChrome));
468
0
469
0
  if (NS_SUCCEEDED(rv) && cancel) {
470
0
    newWindowChrome = nullptr;
471
0
    return NS_ERROR_ABORT;
472
0
  }
473
0
474
0
  newWindowChrome.forget(aResult);
475
0
  return NS_OK;
476
0
}
477
478
/**
479
 * Disable persistence of size/position in popups (determined by
480
 * determining whether the features parameter specifies width or height
481
 * in any way). We consider any overriding of the window's size or position
482
 * in the open call as disabling persistence of those attributes.
483
 * Popup windows (which should not persist size or position) generally set
484
 * the size.
485
 *
486
 * @param aFeatures
487
 *        The features string that was used to open the window.
488
 * @param aTreeOwner
489
 *        The nsIDocShellTreeOwner of the newly opened window. If null,
490
 *        this function is a no-op.
491
 */
492
void
493
nsWindowWatcher::MaybeDisablePersistence(const nsACString& aFeatures,
494
                                         nsIDocShellTreeOwner* aTreeOwner)
495
0
{
496
0
  if (!aTreeOwner) {
497
0
    return;
498
0
  }
499
0
500
0
 // At the moment, the strings "height=" or "width=" never happen
501
0
 // outside a size specification, so we can do this the Q&D way.
502
0
  if (PL_strcasestr(aFeatures.BeginReading(), "width=") ||
503
0
      PL_strcasestr(aFeatures.BeginReading(), "height=")) {
504
0
    aTreeOwner->SetPersistence(false, false, false);
505
0
  }
506
0
}
507
508
NS_IMETHODIMP
509
nsWindowWatcher::OpenWindowWithTabParent(nsITabParent* aOpeningTabParent,
510
                                         const nsACString& aFeatures,
511
                                         bool aCalledFromJS,
512
                                         float aOpenerFullZoom,
513
                                         uint64_t aNextTabParentId,
514
                                         bool aForceNoOpener,
515
                                         nsITabParent** aResult)
516
0
{
517
0
  MOZ_ASSERT(XRE_IsParentProcess());
518
0
  MOZ_ASSERT(mWindowCreator);
519
0
520
0
  if (!nsContentUtils::IsSafeToRunScript()) {
521
0
    nsContentUtils::WarnScriptWasIgnored(nullptr);
522
0
    return NS_ERROR_FAILURE;
523
0
  }
524
0
525
0
  if (NS_WARN_IF(!mWindowCreator)) {
526
0
    return NS_ERROR_UNEXPECTED;
527
0
  }
528
0
529
0
  bool isPrivateBrowsingWindow =
530
0
    Preferences::GetBool("browser.privatebrowsing.autostart");
531
0
532
0
  nsCOMPtr<nsPIDOMWindowOuter> parentWindowOuter;
533
0
  if (aOpeningTabParent) {
534
0
    // We need to examine the window that aOpeningTabParent belongs to in
535
0
    // order to inform us of what kind of window we're going to open.
536
0
    TabParent* openingTab = TabParent::GetFrom(aOpeningTabParent);
537
0
    parentWindowOuter = openingTab->GetParentWindowOuter();
538
0
539
0
    // Propagate the privacy status of the parent window, if
540
0
    // available, to the child.
541
0
    if (!isPrivateBrowsingWindow) {
542
0
      nsCOMPtr<nsILoadContext> parentContext = openingTab->GetLoadContext();
543
0
      if (parentContext) {
544
0
        isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
545
0
      }
546
0
    }
547
0
  }
548
0
549
0
  if (!parentWindowOuter) {
550
0
    // We couldn't find a browser window for the opener, so either we
551
0
    // never were passed aOpeningTabParent, the window is closed,
552
0
    // or it's in the process of closing. Either way, we'll use
553
0
    // the most recently opened browser window instead.
554
0
    parentWindowOuter = nsContentUtils::GetMostRecentNonPBWindow();
555
0
  }
556
0
557
0
  if (NS_WARN_IF(!parentWindowOuter)) {
558
0
    return NS_ERROR_UNEXPECTED;
559
0
  }
560
0
561
0
  nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
562
0
  GetWindowTreeOwner(parentWindowOuter, getter_AddRefs(parentTreeOwner));
563
0
  if (NS_WARN_IF(!parentTreeOwner)) {
564
0
    return NS_ERROR_UNEXPECTED;
565
0
  }
566
0
567
0
  nsCOMPtr<nsIWindowCreator2> windowCreator2(do_QueryInterface(mWindowCreator));
568
0
  if (NS_WARN_IF(!windowCreator2)) {
569
0
    return NS_ERROR_UNEXPECTED;
570
0
  }
571
0
572
0
  uint32_t chromeFlags = CalculateChromeFlagsForChild(aFeatures);
573
0
574
0
  // A content process has asked for a new window, which implies
575
0
  // that the new window will need to be remote.
576
0
  chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
577
0
578
0
  nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
579
0
  nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
580
0
581
0
  CreateChromeWindow(aFeatures, parentChrome, chromeFlags,
582
0
                     aForceNoOpener ? nullptr : aOpeningTabParent, nullptr,
583
0
                     aNextTabParentId,
584
0
                     getter_AddRefs(newWindowChrome));
585
0
586
0
  if (NS_WARN_IF(!newWindowChrome)) {
587
0
    return NS_ERROR_UNEXPECTED;
588
0
  }
589
0
590
0
  nsCOMPtr<nsIDocShellTreeItem> chromeTreeItem = do_GetInterface(newWindowChrome);
591
0
  if (NS_WARN_IF(!chromeTreeItem)) {
592
0
    return NS_ERROR_UNEXPECTED;
593
0
  }
594
0
595
0
  nsCOMPtr<nsIDocShellTreeOwner> chromeTreeOwner;
596
0
  chromeTreeItem->GetTreeOwner(getter_AddRefs(chromeTreeOwner));
597
0
  if (NS_WARN_IF(!chromeTreeOwner)) {
598
0
    return NS_ERROR_UNEXPECTED;
599
0
  }
600
0
601
0
  nsCOMPtr<nsILoadContext> chromeContext = do_QueryInterface(chromeTreeItem);
602
0
  if (NS_WARN_IF(!chromeContext)) {
603
0
    return NS_ERROR_UNEXPECTED;
604
0
  }
605
0
606
0
  chromeContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
607
0
608
0
  // Tabs opened from a content process can only open new windows
609
0
  // that will also run with out-of-process tabs.
610
0
  chromeContext->SetRemoteTabs(true);
611
0
612
0
  MaybeDisablePersistence(aFeatures, chromeTreeOwner);
613
0
614
0
  SizeSpec sizeSpec;
615
0
  CalcSizeSpec(aFeatures, sizeSpec);
616
0
  SizeOpenedWindow(chromeTreeOwner, parentWindowOuter, false, sizeSpec,
617
0
                   Some(aOpenerFullZoom));
618
0
619
0
  nsCOMPtr<nsITabParent> newTabParent;
620
0
  chromeTreeOwner->GetPrimaryTabParent(getter_AddRefs(newTabParent));
621
0
  if (NS_WARN_IF(!newTabParent)) {
622
0
    return NS_ERROR_UNEXPECTED;
623
0
  }
624
0
625
0
  newTabParent.forget(aResult);
626
0
  return NS_OK;
627
0
}
628
629
nsresult
630
nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent,
631
                                    const char* aUrl,
632
                                    const char* aName,
633
                                    const char* aFeatures,
634
                                    bool aCalledFromJS,
635
                                    bool aDialog,
636
                                    bool aNavigate,
637
                                    nsIArray* aArgv,
638
                                    bool aIsPopupSpam,
639
                                    bool aForceNoOpener,
640
                                    nsDocShellLoadInfo* aLoadInfo,
641
                                    mozIDOMWindowProxy** aResult)
642
0
{
643
0
  nsresult rv = NS_OK;
644
0
  bool isNewToplevelWindow = false;
645
0
  bool windowIsNew = false;
646
0
  bool windowNeedsName = false;
647
0
  bool windowIsModal = false;
648
0
  bool uriToLoadIsChrome = false;
649
0
  bool windowIsModalContentDialog = false;
650
0
651
0
  uint32_t chromeFlags;
652
0
  nsAutoString name;          // string version of aName
653
0
  nsAutoCString features;     // string version of aFeatures
654
0
  nsCOMPtr<nsIURI> uriToLoad; // from aUrl, if any
655
0
  nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; // from the parent window, if any
656
0
  nsCOMPtr<nsIDocShellTreeItem> newDocShellItem; // from the new window
657
0
658
0
  nsCOMPtr<nsPIDOMWindowOuter> parent =
659
0
    aParent ? nsPIDOMWindowOuter::From(aParent) : nullptr;
660
0
661
0
  NS_ENSURE_ARG_POINTER(aResult);
662
0
  *aResult = 0;
663
0
664
0
  if (!nsContentUtils::IsSafeToRunScript()) {
665
0
    nsContentUtils::WarnScriptWasIgnored(nullptr);
666
0
    return NS_ERROR_FAILURE;
667
0
  }
668
0
669
0
  GetWindowTreeOwner(parent, getter_AddRefs(parentTreeOwner));
670
0
671
0
  // We expect TabParent to have provided us the absolute URI of the window
672
0
  // we're to open, so there's no need to call URIfromURL (or more importantly,
673
0
  // to check for a chrome URI, which cannot be opened from a remote tab).
674
0
  if (aUrl) {
675
0
    rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad));
676
0
    if (NS_FAILED(rv)) {
677
0
      return rv;
678
0
    }
679
0
    uriToLoad->SchemeIs("chrome", &uriToLoadIsChrome);
680
0
  }
681
0
682
0
  bool nameSpecified = false;
683
0
  if (aName) {
684
0
    CopyUTF8toUTF16(MakeStringSpan(aName), name);
685
0
    nameSpecified = true;
686
0
  } else {
687
0
    name.SetIsVoid(true);
688
0
  }
689
0
690
0
  if (aFeatures) {
691
0
    features.Assign(aFeatures);
692
0
    features.StripWhitespace();
693
0
  } else {
694
0
    features.SetIsVoid(true);
695
0
  }
696
0
697
0
  // try to find an extant window with the given name
698
0
  nsCOMPtr<nsPIDOMWindowOuter> foundWindow =
699
0
    SafeGetWindowByName(name, aForceNoOpener, aParent);
700
0
  GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
701
0
702
0
  // Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI.
703
0
  // The state of the window can change before this call and if we are blocked
704
0
  // because of sandboxing, we wouldn't want that to happen.
705
0
  nsCOMPtr<nsPIDOMWindowOuter> parentWindow =
706
0
    aParent ? nsPIDOMWindowOuter::From(aParent) : nullptr;
707
0
  nsCOMPtr<nsIDocShell> parentDocShell;
708
0
  if (parentWindow) {
709
0
    parentDocShell = parentWindow->GetDocShell();
710
0
    if (parentDocShell) {
711
0
      nsCOMPtr<nsIDocShell> foundDocShell = do_QueryInterface(newDocShellItem);
712
0
      if (parentDocShell->IsSandboxedFrom(foundDocShell)) {
713
0
        return NS_ERROR_DOM_INVALID_ACCESS_ERR;
714
0
      }
715
0
    }
716
0
  }
717
0
718
0
  // no extant window? make a new one.
719
0
720
0
  // If no parent, consider it chrome when running in the parent process.
721
0
  bool hasChromeParent = XRE_IsContentProcess() ? false : true;
722
0
  if (aParent) {
723
0
    // Check if the parent document has chrome privileges.
724
0
    nsIDocument* doc = parentWindow->GetDoc();
725
0
    hasChromeParent = doc && nsContentUtils::IsChromeDoc(doc);
726
0
  }
727
0
728
0
  bool isCallerChrome = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
729
0
730
0
  // Make sure we calculate the chromeFlags *before* we push the
731
0
  // callee context onto the context stack so that
732
0
  // the calculation sees the actual caller when doing its
733
0
  // security checks.
734
0
  if (isCallerChrome && XRE_IsParentProcess()) {
735
0
    chromeFlags = CalculateChromeFlagsForParent(aParent, features,
736
0
                                                aDialog, uriToLoadIsChrome,
737
0
                                                hasChromeParent, aCalledFromJS);
738
0
  } else {
739
0
    chromeFlags = CalculateChromeFlagsForChild(features);
740
0
741
0
    // Until ShowModalDialog is removed, it's still possible for content to
742
0
    // request dialogs, but only in single-process mode.
743
0
    if (aDialog) {
744
0
      MOZ_ASSERT(XRE_IsParentProcess());
745
0
      chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
746
0
    }
747
0
  }
748
0
749
0
  SizeSpec sizeSpec;
750
0
  CalcSizeSpec(features, sizeSpec);
751
0
752
0
  // XXXbz Why is an AutoJSAPI good enough here?  Wouldn't AutoEntryScript (so
753
0
  // we affect the entry global) make more sense?  Or do we just want to affect
754
0
  // GetSubjectPrincipal()?
755
0
  dom::AutoJSAPI jsapiChromeGuard;
756
0
757
0
  bool windowTypeIsChrome =
758
0
    chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
759
0
  if (isCallerChrome && !hasChromeParent && !windowTypeIsChrome) {
760
0
    // open() is called from chrome on a non-chrome window, initialize an
761
0
    // AutoJSAPI with the callee to prevent the caller's privileges from leaking
762
0
    // into code that runs while opening the new window.
763
0
    //
764
0
    // The reasoning for this is in bug 289204. Basically, chrome sometimes does
765
0
    // someContentWindow.open(untrustedURL), and wants to be insulated from nasty
766
0
    // javascript: URLs and such. But there are also cases where we create a
767
0
    // window parented to a content window (such as a download dialog), usually
768
0
    // directly with nsIWindowWatcher. In those cases, we want the principal of
769
0
    // the initial about:blank document to be system, so that the subsequent XUL
770
0
    // load can reuse the inner window and avoid blowing away expandos. As such,
771
0
    // we decide whether to load with the principal of the caller or of the parent
772
0
    // based on whether the docshell type is chrome or content.
773
0
774
0
    nsCOMPtr<nsIGlobalObject> parentGlobalObject = do_QueryInterface(aParent);
775
0
    if (!aParent) {
776
0
      jsapiChromeGuard.Init();
777
0
    } else if (NS_WARN_IF(!jsapiChromeGuard.Init(parentGlobalObject))) {
778
0
      return NS_ERROR_UNEXPECTED;
779
0
    }
780
0
  }
781
0
782
0
  uint32_t activeDocsSandboxFlags = 0;
783
0
  if (!newDocShellItem) {
784
0
    // We're going to either open up a new window ourselves or ask a
785
0
    // nsIWindowProvider for one.  In either case, we'll want to set the right
786
0
    // name on it.
787
0
    windowNeedsName = true;
788
0
789
0
    // If the parent trying to open a new window is sandboxed
790
0
    // without 'allow-popups', this is not allowed and we fail here.
791
0
    if (aParent) {
792
0
      if (nsIDocument* doc = parentWindow->GetDoc()) {
793
0
        // Save sandbox flags for copying to new browsing context (docShell).
794
0
        activeDocsSandboxFlags = doc->GetSandboxFlags();
795
0
        if (activeDocsSandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
796
0
          return NS_ERROR_DOM_INVALID_ACCESS_ERR;
797
0
        }
798
0
      }
799
0
    }
800
0
801
0
    // Now check whether it's ok to ask a window provider for a window.  Don't
802
0
    // do it if we're opening a dialog or if our parent is a chrome window or
803
0
    // if we're opening something that has modal, dialog, or chrome flags set.
804
0
    nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(aParent);
805
0
    if (!aDialog && !chromeWin &&
806
0
        !(chromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
807
0
                         nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
808
0
                         nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
809
0
      nsCOMPtr<nsIWindowProvider> provider;
810
0
      if (parentTreeOwner) {
811
0
        provider = do_GetInterface(parentTreeOwner);
812
0
      } else if (XRE_IsContentProcess()) {
813
0
        // we're in a content process but we don't have a tabchild we can
814
0
        // use.
815
0
        provider = nsContentUtils::GetWindowProviderForContentProcess();
816
0
      }
817
0
818
0
      if (provider) {
819
0
        nsCOMPtr<mozIDOMWindowProxy> newWindow;
820
0
        rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS,
821
0
                                     sizeSpec.PositionSpecified(),
822
0
                                     sizeSpec.SizeSpecified(),
823
0
                                     uriToLoad, name, features, aForceNoOpener,
824
0
                                     aLoadInfo, &windowIsNew,
825
0
                                     getter_AddRefs(newWindow));
826
0
827
0
        if (NS_SUCCEEDED(rv)) {
828
0
          GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
829
0
          if (windowIsNew && newDocShellItem) {
830
0
            // Make sure to stop any loads happening in this window that the
831
0
            // window provider might have started.  Otherwise if our caller
832
0
            // manipulates the window it just opened and then the load
833
0
            // completes their stuff will get blown away.
834
0
            nsCOMPtr<nsIWebNavigation> webNav =
835
0
              do_QueryInterface(newDocShellItem);
836
0
            webNav->Stop(nsIWebNavigation::STOP_NETWORK);
837
0
          }
838
0
839
0
          // If this is a new window, but it's incompatible with the current
840
0
          // userContextId, we ignore it and we pretend that nothing has been
841
0
          // returned by ProvideWindow.
842
0
          if (!windowIsNew && newDocShellItem) {
843
0
            nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(newDocShellItem);
844
0
            if (!CheckUserContextCompatibility(docShell)) {
845
0
              newWindow = nullptr;
846
0
              newDocShellItem = nullptr;
847
0
              windowIsNew = false;
848
0
            }
849
0
          }
850
0
851
0
        } else if (rv == NS_ERROR_ABORT) {
852
0
          // NS_ERROR_ABORT means the window provider has flat-out rejected
853
0
          // the open-window call and we should bail.  Don't return an error
854
0
          // here, because our caller may propagate that error, which might
855
0
          // cause e.g. window.open to throw!  Just return null for our out
856
0
          // param.
857
0
          return NS_OK;
858
0
        }
859
0
      }
860
0
    }
861
0
  }
862
0
863
0
  bool newWindowShouldBeModal = false;
864
0
  bool parentIsModal = false;
865
0
  if (!newDocShellItem) {
866
0
    windowIsNew = true;
867
0
    isNewToplevelWindow = true;
868
0
869
0
    nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
870
0
871
0
    // is the parent (if any) modal? if so, we must be, too.
872
0
    bool weAreModal = (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) != 0;
873
0
    newWindowShouldBeModal = weAreModal;
874
0
    if (!weAreModal && parentChrome) {
875
0
      parentChrome->IsWindowModal(&weAreModal);
876
0
      parentIsModal = weAreModal;
877
0
    }
878
0
879
0
    if (weAreModal) {
880
0
      windowIsModal = true;
881
0
      // in case we added this because weAreModal
882
0
      chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL |
883
0
                     nsIWebBrowserChrome::CHROME_DEPENDENT;
884
0
    }
885
0
886
0
    // Make sure to not create modal windows if our parent is invisible and
887
0
    // isn't a chrome window.  Otherwise we can end up in a bizarre situation
888
0
    // where we can't shut down because an invisible window is open.  If
889
0
    // someone tries to do this, throw.
890
0
    if (!hasChromeParent && (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)) {
891
0
      nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(parentTreeOwner));
892
0
      nsCOMPtr<nsIWidget> parentWidget;
893
0
      if (parentWindow) {
894
0
        parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
895
0
      }
896
0
      // NOTE: the logic for this visibility check is duplicated in
897
0
      // nsIDOMWindowUtils::isParentWindowMainWidgetVisible - if we change
898
0
      // how a window is determined "visible" in this context then we should
899
0
      // also adjust that attribute and/or any consumers of it...
900
0
      if (parentWidget && !parentWidget->IsVisible()) {
901
0
        return NS_ERROR_NOT_AVAILABLE;
902
0
      }
903
0
    }
904
0
905
0
    NS_ASSERTION(mWindowCreator,
906
0
                 "attempted to open a new window with no WindowCreator");
907
0
    rv = NS_ERROR_FAILURE;
908
0
    if (mWindowCreator) {
909
0
      nsCOMPtr<nsIWebBrowserChrome> newChrome;
910
0
911
0
      nsCOMPtr<nsPIDOMWindowInner> parentTopInnerWindow;
912
0
      if (parentWindow) {
913
0
        nsCOMPtr<nsPIDOMWindowOuter> parentTopWindow = parentWindow->GetTop();
914
0
        if (parentTopWindow) {
915
0
          parentTopInnerWindow = parentTopWindow->GetCurrentInnerWindow();
916
0
        }
917
0
      }
918
0
919
0
      if (parentTopInnerWindow) {
920
0
        parentTopInnerWindow->Suspend();
921
0
      }
922
0
923
0
      /* If the window creator is an nsIWindowCreator2, we can give it
924
0
         some hints. The only hint at this time is whether the opening window
925
0
         is in a situation that's likely to mean this is an unrequested
926
0
         popup window we're creating. However we're not completely honest:
927
0
         we clear that indicator if the opener is chrome, so that the
928
0
         downstream consumer can treat the indicator to mean simply
929
0
         that the new window is subject to popup control. */
930
0
      nsCOMPtr<nsIWindowCreator2> windowCreator2(
931
0
        do_QueryInterface(mWindowCreator));
932
0
      if (windowCreator2) {
933
0
        mozIDOMWindowProxy* openerWindow = aForceNoOpener ? nullptr : aParent;
934
0
        rv = CreateChromeWindow(features, parentChrome, chromeFlags,
935
0
                                nullptr, openerWindow, 0,
936
0
                                getter_AddRefs(newChrome));
937
0
938
0
      } else {
939
0
        rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
940
0
                                                getter_AddRefs(newChrome));
941
0
      }
942
0
943
0
      if (parentTopInnerWindow) {
944
0
        parentTopInnerWindow->Resume();
945
0
      }
946
0
947
0
      if (newChrome) {
948
0
        nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(newChrome);
949
0
        if (xulWin) {
950
0
          nsCOMPtr<nsIXULBrowserWindow> xulBrowserWin;
951
0
          xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin));
952
0
          if (xulBrowserWin) {
953
0
            nsPIDOMWindowOuter* openerWindow = aForceNoOpener ? nullptr : parentWindow.get();
954
0
            xulBrowserWin->ForceInitialBrowserNonRemote(openerWindow);
955
0
          }
956
0
        }
957
0
        /* It might be a chrome nsXULWindow, in which case it won't have
958
0
            an nsIDOMWindow (primary content shell). But in that case, it'll
959
0
            be able to hand over an nsIDocShellTreeItem directly. */
960
0
        nsCOMPtr<nsPIDOMWindowOuter> newWindow(do_GetInterface(newChrome));
961
0
        if (newWindow) {
962
0
          GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
963
0
        }
964
0
        if (!newDocShellItem) {
965
0
          newDocShellItem = do_GetInterface(newChrome);
966
0
        }
967
0
        if (!newDocShellItem) {
968
0
          rv = NS_ERROR_FAILURE;
969
0
        }
970
0
      }
971
0
    }
972
0
  }
973
0
974
0
  // better have a window to use by this point
975
0
  if (!newDocShellItem) {
976
0
    return rv;
977
0
  }
978
0
979
0
  nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
980
0
  NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED);
981
0
982
0
  // If our parent is sandboxed, set it as the one permitted sandboxed navigator
983
0
  // on the new window we're opening.
984
0
  if (activeDocsSandboxFlags && parentWindow) {
985
0
    newDocShell->SetOnePermittedSandboxedNavigator(
986
0
      parentWindow->GetDocShell());
987
0
  }
988
0
989
0
  // Copy sandbox flags to the new window if activeDocsSandboxFlags says to do
990
0
  // so.  Note that it's only nonzero if the window is new, so clobbering
991
0
  // sandbox flags on the window makes sense in that case.
992
0
  if (activeDocsSandboxFlags &
993
0
        SANDBOX_PROPAGATES_TO_AUXILIARY_BROWSING_CONTEXTS) {
994
0
    newDocShell->SetSandboxFlags(activeDocsSandboxFlags);
995
0
  }
996
0
997
0
  rv = ReadyOpenedDocShellItem(newDocShellItem, parentWindow, windowIsNew,
998
0
                               aForceNoOpener, aResult);
999
0
  if (NS_FAILED(rv)) {
1000
0
    return rv;
1001
0
  }
1002
0
1003
0
  if (isNewToplevelWindow) {
1004
0
    nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
1005
0
    newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
1006
0
    MaybeDisablePersistence(features, newTreeOwner);
1007
0
  }
1008
0
1009
0
  if ((aDialog || windowIsModalContentDialog) && aArgv) {
1010
0
    // Set the args on the new window.
1011
0
    nsCOMPtr<nsPIDOMWindowOuter> piwin(do_QueryInterface(*aResult));
1012
0
    NS_ENSURE_TRUE(piwin, NS_ERROR_UNEXPECTED);
1013
0
1014
0
    rv = piwin->SetArguments(aArgv);
1015
0
    NS_ENSURE_SUCCESS(rv, rv);
1016
0
  }
1017
0
1018
0
  /* allow a window that we found by name to keep its name (important for cases
1019
0
     like _self where the given name is different (and invalid)).  Also, _blank
1020
0
     is not a window name. */
1021
0
  if (windowNeedsName) {
1022
0
    if (nameSpecified && !name.LowerCaseEqualsLiteral("_blank")) {
1023
0
      newDocShellItem->SetName(name);
1024
0
    } else {
1025
0
      newDocShellItem->SetName(EmptyString());
1026
0
    }
1027
0
  }
1028
0
1029
0
  // Now we have to set the right opener principal on the new window.  Note
1030
0
  // that we have to do this _before_ starting any URI loads, thanks to the
1031
0
  // sync nature of javascript: loads.
1032
0
  //
1033
0
  // Note: The check for the current JSContext isn't necessarily sensical.
1034
0
  // It's just designed to preserve old semantics during a mass-conversion
1035
0
  // patch.
1036
0
  nsCOMPtr<nsIPrincipal> subjectPrincipal =
1037
0
    nsContentUtils::GetCurrentJSContext() ? nsContentUtils::SubjectPrincipal() :
1038
0
                                            nullptr;
1039
0
1040
0
  bool isPrivateBrowsingWindow = false;
1041
0
1042
0
  if (windowIsNew) {
1043
0
    auto* docShell = static_cast<nsDocShell*>(newDocShell.get());
1044
0
1045
0
    // If this is not a chrome docShell, we apply originAttributes from the
1046
0
    // subjectPrincipal unless if it's an expanded or system principal.
1047
0
    if (subjectPrincipal &&
1048
0
        !nsContentUtils::IsSystemOrExpandedPrincipal(subjectPrincipal) &&
1049
0
        docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
1050
0
      isPrivateBrowsingWindow =
1051
0
        !!subjectPrincipal->OriginAttributesRef().mPrivateBrowsingId;
1052
0
      docShell->SetOriginAttributes(subjectPrincipal->OriginAttributesRef());
1053
0
    } else {
1054
0
      nsCOMPtr<nsIDocShellTreeItem> parentItem;
1055
0
      GetWindowTreeItem(aParent, getter_AddRefs(parentItem));
1056
0
      nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(parentItem);
1057
0
      if (parentContext) {
1058
0
        isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
1059
0
      }
1060
0
    }
1061
0
1062
0
    bool autoPrivateBrowsing =
1063
0
      Preferences::GetBool("browser.privatebrowsing.autostart");
1064
0
1065
0
    if (!autoPrivateBrowsing &&
1066
0
        (chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW)) {
1067
0
      isPrivateBrowsingWindow = false;
1068
0
    } else if (autoPrivateBrowsing ||
1069
0
               (chromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW)) {
1070
0
      isPrivateBrowsingWindow = true;
1071
0
    }
1072
0
1073
0
    // Now set the opener principal on the new window.  Note that we need to do
1074
0
    // this no matter whether we were opened from JS; if there is nothing on
1075
0
    // the JS stack, just use the principal of our parent window.  In those
1076
0
    // cases we do _not_ set the parent window principal as the owner of the
1077
0
    // load--since we really don't know who the owner is, just leave it null.
1078
0
    nsCOMPtr<nsPIDOMWindowOuter> newWindow = do_QueryInterface(*aResult);
1079
0
    NS_ASSERTION(newWindow == newDocShell->GetWindow(), "Different windows??");
1080
0
1081
0
    // The principal of the initial about:blank document gets set up in
1082
0
    // nsWindowWatcher::AddWindow. Make sure to call it. In the common case
1083
0
    // this call already happened when the window was created, but
1084
0
    // SetInitialPrincipalToSubject is safe to call multiple times.
1085
0
    if (newWindow) {
1086
0
      newWindow->SetInitialPrincipalToSubject();
1087
0
      if (aIsPopupSpam) {
1088
0
        nsGlobalWindowOuter* globalWin = nsGlobalWindowOuter::Cast(newWindow);
1089
0
        MOZ_ASSERT(!globalWin->IsPopupSpamWindow(),
1090
0
                   "Who marked it as popup spam already???");
1091
0
        if (!globalWin->IsPopupSpamWindow()) { // Make sure we don't mess up our
1092
0
                                               // counter even if the above
1093
0
                                               // assert fails.
1094
0
          globalWin->SetIsPopupSpamWindow(true);
1095
0
        }
1096
0
      }
1097
0
    }
1098
0
  }
1099
0
1100
0
  // We rely on CalculateChromeFlags to decide whether remote (out-of-process)
1101
0
  // tabs should be used.
1102
0
  bool isRemoteWindow =
1103
0
    !!(chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
1104
0
1105
0
  if (isNewToplevelWindow) {
1106
0
    nsCOMPtr<nsIDocShellTreeItem> childRoot;
1107
0
    newDocShellItem->GetRootTreeItem(getter_AddRefs(childRoot));
1108
0
    nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(childRoot);
1109
0
    if (childContext) {
1110
0
      childContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
1111
0
      childContext->SetRemoteTabs(isRemoteWindow);
1112
0
    }
1113
0
  } else if (windowIsNew) {
1114
0
    nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(newDocShellItem);
1115
0
    if (childContext) {
1116
0
      childContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
1117
0
      childContext->SetRemoteTabs(isRemoteWindow);
1118
0
    }
1119
0
  }
1120
0
1121
0
  RefPtr<nsDocShellLoadInfo> loadInfo = aLoadInfo;
1122
0
  if (uriToLoad && aNavigate && !loadInfo) {
1123
0
    loadInfo = new nsDocShellLoadInfo();
1124
0
1125
0
    if (subjectPrincipal) {
1126
0
      loadInfo->SetTriggeringPrincipal(subjectPrincipal);
1127
0
    }
1128
0
1129
0
    /* use the URL from the *extant* document, if any. The usual accessor
1130
0
       GetDocument will synchronously create an about:blank document if
1131
0
       it has no better answer, and we only care about a real document.
1132
0
       Also using GetDocument to force document creation seems to
1133
0
       screw up focus in the hidden window; see bug 36016.
1134
0
    */
1135
0
    nsCOMPtr<nsIDocument> doc = GetEntryDocument();
1136
0
    if (!doc && parentWindow) {
1137
0
      doc = parentWindow->GetExtantDoc();
1138
0
    }
1139
0
    if (doc) {
1140
0
      // Set the referrer
1141
0
      loadInfo->SetReferrer(doc->GetDocumentURI());
1142
0
      loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
1143
0
    }
1144
0
  }
1145
0
1146
0
  if (isNewToplevelWindow) {
1147
0
    // Notify observers that the window is open and ready.
1148
0
    // The window has not yet started to load a document.
1149
0
    nsCOMPtr<nsIObserverService> obsSvc =
1150
0
      mozilla::services::GetObserverService();
1151
0
    if (obsSvc) {
1152
0
      obsSvc->NotifyObservers(*aResult, "toplevel-window-ready", nullptr);
1153
0
    }
1154
0
  }
1155
0
1156
0
  // Before loading the URI we want to be 100% sure that we use the correct
1157
0
  // userContextId.
1158
0
  MOZ_ASSERT(CheckUserContextCompatibility(newDocShell));
1159
0
1160
0
  // If this tab or window has been opened by a window.open call, we have to provide
1161
0
  // all the data needed to send a webNavigation.onCreatedNavigationTarget event.
1162
0
  if (parentDocShell && newDocShellItem) {
1163
0
    nsCOMPtr<nsIObserverService> obsSvc =
1164
0
      mozilla::services::GetObserverService();
1165
0
1166
0
    if (obsSvc) {
1167
0
      RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
1168
0
1169
0
      if (uriToLoad) {
1170
0
        // The url notified in the webNavigation.onCreatedNavigationTarget event.
1171
0
        props->SetPropertyAsACString(NS_LITERAL_STRING("url"),
1172
0
                                     uriToLoad->GetSpecOrDefault());
1173
0
      }
1174
0
1175
0
      props->SetPropertyAsInterface(NS_LITERAL_STRING("sourceTabDocShell"), parentDocShell);
1176
0
      props->SetPropertyAsInterface(NS_LITERAL_STRING("createdTabDocShell"), newDocShellItem);
1177
0
1178
0
      obsSvc->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
1179
0
                              "webNavigation-createdNavigationTarget-from-js", nullptr);
1180
0
    }
1181
0
  }
1182
0
1183
0
  if (uriToLoad && aNavigate) {
1184
0
    newDocShell->LoadURI(
1185
0
      uriToLoad,
1186
0
      loadInfo,
1187
0
      windowIsNew ?
1188
0
        static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD) :
1189
0
        static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_NONE),
1190
0
      true);
1191
0
  }
1192
0
1193
0
  // Copy the current session storage for the current domain. Don't perform the
1194
0
  // copy if we're forcing noopener, however.
1195
0
  if (!aForceNoOpener && subjectPrincipal && parentDocShell) {
1196
0
    nsCOMPtr<nsIDOMStorageManager> parentStorageManager =
1197
0
      do_QueryInterface(parentDocShell);
1198
0
    nsCOMPtr<nsIDOMStorageManager> newStorageManager =
1199
0
      do_QueryInterface(newDocShell);
1200
0
1201
0
    if (parentStorageManager && newStorageManager) {
1202
0
      RefPtr<Storage> storage;
1203
0
      nsCOMPtr<nsPIDOMWindowInner> pInnerWin = parentWindow->GetCurrentInnerWindow();
1204
0
      parentStorageManager->GetStorage(pInnerWin, subjectPrincipal,
1205
0
                                       isPrivateBrowsingWindow,
1206
0
                                       getter_AddRefs(storage));
1207
0
      if (storage) {
1208
0
        newStorageManager->CloneStorage(storage);
1209
0
      }
1210
0
    }
1211
0
  }
1212
0
1213
0
  if (isNewToplevelWindow) {
1214
0
    nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
1215
0
    newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
1216
0
    SizeOpenedWindow(newTreeOwner, aParent, isCallerChrome, sizeSpec);
1217
0
  }
1218
0
1219
0
  // XXXbz isn't windowIsModal always true when windowIsModalContentDialog?
1220
0
  if (windowIsModal || windowIsModalContentDialog) {
1221
0
    nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
1222
0
    newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
1223
0
    nsCOMPtr<nsIWebBrowserChrome> newChrome(do_GetInterface(newTreeOwner));
1224
0
1225
0
    // Throw an exception here if no web browser chrome is available,
1226
0
    // we need that to show a modal window.
1227
0
    NS_ENSURE_TRUE(newChrome, NS_ERROR_NOT_AVAILABLE);
1228
0
1229
0
    // Dispatch dialog events etc, but we only want to do that if
1230
0
    // we're opening a modal content window (the helper classes are
1231
0
    // no-ops if given no window), for chrome dialogs we don't want to
1232
0
    // do any of that (it's done elsewhere for us).
1233
0
    // Make sure we maintain the state on an outer window, because
1234
0
    // that's where it lives; inner windows assert if you try to
1235
0
    // maintain the state on them.
1236
0
    nsAutoWindowStateHelper windowStateHelper(
1237
0
      parentWindow ? parentWindow->GetOuterWindow() : nullptr);
1238
0
1239
0
    if (!windowStateHelper.DefaultEnabled()) {
1240
0
      // Default to cancel not opening the modal window.
1241
0
      NS_RELEASE(*aResult);
1242
0
1243
0
      return NS_OK;
1244
0
    }
1245
0
1246
0
    bool isAppModal = false;
1247
0
    nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(newTreeOwner));
1248
0
    nsCOMPtr<nsIWidget> parentWidget;
1249
0
    if (parentWindow) {
1250
0
      parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
1251
0
      if (parentWidget) {
1252
0
        isAppModal = parentWidget->IsRunningAppModal();
1253
0
      }
1254
0
    }
1255
0
    if (parentWidget &&
1256
0
        ((!newWindowShouldBeModal && parentIsModal) || isAppModal)) {
1257
0
      parentWidget->SetFakeModal(true);
1258
0
    } else {
1259
0
      // Reset popup state while opening a modal dialog, and firing
1260
0
      // events about the dialog, to prevent the current state from
1261
0
      // being active the whole time a modal dialog is open.
1262
0
      nsAutoPopupStatePusher popupStatePusher(openAbused);
1263
0
1264
0
      newChrome->ShowAsModal();
1265
0
    }
1266
0
  }
1267
0
1268
0
  if (aForceNoOpener && windowIsNew) {
1269
0
    NS_RELEASE(*aResult);
1270
0
  }
1271
0
1272
0
  return NS_OK;
1273
0
}
1274
1275
NS_IMETHODIMP
1276
nsWindowWatcher::RegisterNotification(nsIObserver* aObserver)
1277
0
{
1278
0
  // just a convenience method; it delegates to nsIObserverService
1279
0
1280
0
  if (!aObserver) {
1281
0
    return NS_ERROR_INVALID_ARG;
1282
0
  }
1283
0
1284
0
  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1285
0
  if (!os) {
1286
0
    return NS_ERROR_FAILURE;
1287
0
  }
1288
0
1289
0
  nsresult rv = os->AddObserver(aObserver, "domwindowopened", false);
1290
0
  if (NS_SUCCEEDED(rv)) {
1291
0
    rv = os->AddObserver(aObserver, "domwindowclosed", false);
1292
0
  }
1293
0
1294
0
  return rv;
1295
0
}
1296
1297
NS_IMETHODIMP
1298
nsWindowWatcher::UnregisterNotification(nsIObserver* aObserver)
1299
0
{
1300
0
  // just a convenience method; it delegates to nsIObserverService
1301
0
1302
0
  if (!aObserver) {
1303
0
    return NS_ERROR_INVALID_ARG;
1304
0
  }
1305
0
1306
0
  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1307
0
  if (!os) {
1308
0
    return NS_ERROR_FAILURE;
1309
0
  }
1310
0
1311
0
  os->RemoveObserver(aObserver, "domwindowopened");
1312
0
  os->RemoveObserver(aObserver, "domwindowclosed");
1313
0
1314
0
  return NS_OK;
1315
0
}
1316
1317
NS_IMETHODIMP
1318
nsWindowWatcher::GetWindowEnumerator(nsISimpleEnumerator** aResult)
1319
0
{
1320
0
  if (!aResult) {
1321
0
    return NS_ERROR_INVALID_ARG;
1322
0
  }
1323
0
1324
0
  MutexAutoLock lock(mListLock);
1325
0
  nsWatcherWindowEnumerator* enumerator = new nsWatcherWindowEnumerator(this);
1326
0
  if (enumerator) {
1327
0
    return CallQueryInterface(enumerator, aResult);
1328
0
  }
1329
0
1330
0
  return NS_ERROR_OUT_OF_MEMORY;
1331
0
}
1332
1333
NS_IMETHODIMP
1334
nsWindowWatcher::GetNewPrompter(mozIDOMWindowProxy* aParent, nsIPrompt** aResult)
1335
0
{
1336
0
  // This is for backwards compat only. Callers should just use the prompt
1337
0
  // service directly.
1338
0
  nsresult rv;
1339
0
  nsCOMPtr<nsIPromptFactory> factory =
1340
0
    do_GetService("@mozilla.org/prompter;1", &rv);
1341
0
  NS_ENSURE_SUCCESS(rv, rv);
1342
0
  return factory->GetPrompt(aParent, NS_GET_IID(nsIPrompt),
1343
0
                            reinterpret_cast<void**>(aResult));
1344
0
}
1345
1346
NS_IMETHODIMP
1347
nsWindowWatcher::GetNewAuthPrompter(mozIDOMWindowProxy* aParent,
1348
                                    nsIAuthPrompt** aResult)
1349
0
{
1350
0
  // This is for backwards compat only. Callers should just use the prompt
1351
0
  // service directly.
1352
0
  nsresult rv;
1353
0
  nsCOMPtr<nsIPromptFactory> factory =
1354
0
    do_GetService("@mozilla.org/prompter;1", &rv);
1355
0
  NS_ENSURE_SUCCESS(rv, rv);
1356
0
  return factory->GetPrompt(aParent, NS_GET_IID(nsIAuthPrompt),
1357
0
                            reinterpret_cast<void**>(aResult));
1358
0
}
1359
1360
NS_IMETHODIMP
1361
nsWindowWatcher::GetPrompt(mozIDOMWindowProxy* aParent, const nsIID& aIID,
1362
                           void** aResult)
1363
0
{
1364
0
  // This is for backwards compat only. Callers should just use the prompt
1365
0
  // service directly.
1366
0
  nsresult rv;
1367
0
  nsCOMPtr<nsIPromptFactory> factory =
1368
0
    do_GetService("@mozilla.org/prompter;1", &rv);
1369
0
  NS_ENSURE_SUCCESS(rv, rv);
1370
0
  rv = factory->GetPrompt(aParent, aIID, aResult);
1371
0
1372
0
  // Allow for an embedding implementation to not support nsIAuthPrompt2.
1373
0
  if (rv == NS_NOINTERFACE && aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
1374
0
    nsCOMPtr<nsIAuthPrompt> oldPrompt;
1375
0
    rv = factory->GetPrompt(
1376
0
      aParent, NS_GET_IID(nsIAuthPrompt), getter_AddRefs(oldPrompt));
1377
0
    NS_ENSURE_SUCCESS(rv, rv);
1378
0
1379
0
    NS_WrapAuthPrompt(oldPrompt, reinterpret_cast<nsIAuthPrompt2**>(aResult));
1380
0
    if (!*aResult) {
1381
0
      rv = NS_ERROR_NOT_AVAILABLE;
1382
0
    }
1383
0
  }
1384
0
  return rv;
1385
0
}
1386
1387
NS_IMETHODIMP
1388
nsWindowWatcher::SetWindowCreator(nsIWindowCreator* aCreator)
1389
0
{
1390
0
  mWindowCreator = aCreator;
1391
0
  return NS_OK;
1392
0
}
1393
1394
NS_IMETHODIMP
1395
nsWindowWatcher::HasWindowCreator(bool* aResult)
1396
0
{
1397
0
  *aResult = mWindowCreator;
1398
0
  return NS_OK;
1399
0
}
1400
1401
NS_IMETHODIMP
1402
nsWindowWatcher::GetActiveWindow(mozIDOMWindowProxy** aActiveWindow)
1403
0
{
1404
0
  *aActiveWindow = nullptr;
1405
0
  nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
1406
0
  if (fm) {
1407
0
    return fm->GetActiveWindow(aActiveWindow);
1408
0
  }
1409
0
  return NS_OK;
1410
0
}
1411
1412
NS_IMETHODIMP
1413
nsWindowWatcher::SetActiveWindow(mozIDOMWindowProxy* aActiveWindow)
1414
0
{
1415
0
  nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
1416
0
  if (fm) {
1417
0
    return fm->SetActiveWindow(aActiveWindow);
1418
0
  }
1419
0
  return NS_OK;
1420
0
}
1421
1422
NS_IMETHODIMP
1423
nsWindowWatcher::AddWindow(mozIDOMWindowProxy* aWindow, nsIWebBrowserChrome* aChrome)
1424
0
{
1425
0
  if (!aWindow) {
1426
0
    return NS_ERROR_INVALID_ARG;
1427
0
  }
1428
0
1429
0
  {
1430
0
    nsWatcherWindowEntry* info;
1431
0
    MutexAutoLock lock(mListLock);
1432
0
1433
0
    // if we already have an entry for this window, adjust
1434
0
    // its chrome mapping and return
1435
0
    info = FindWindowEntry(aWindow);
1436
0
    if (info) {
1437
0
      nsCOMPtr<nsISupportsWeakReference> supportsweak(
1438
0
        do_QueryInterface(aChrome));
1439
0
      if (supportsweak) {
1440
0
        supportsweak->GetWeakReference(getter_AddRefs(info->mChromeWeak));
1441
0
      } else {
1442
0
        info->mChrome = aChrome;
1443
0
        info->mChromeWeak = nullptr;
1444
0
      }
1445
0
      return NS_OK;
1446
0
    }
1447
0
1448
0
    // create a window info struct and add it to the list of windows
1449
0
    info = new nsWatcherWindowEntry(aWindow, aChrome);
1450
0
    if (!info) {
1451
0
      return NS_ERROR_OUT_OF_MEMORY;
1452
0
    }
1453
0
1454
0
    if (mOldestWindow) {
1455
0
      info->InsertAfter(mOldestWindow->mOlder);
1456
0
    } else {
1457
0
      mOldestWindow = info;
1458
0
    }
1459
0
  } // leave the mListLock
1460
0
1461
0
  // a window being added to us signifies a newly opened window.
1462
0
  // send notifications.
1463
0
  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1464
0
  if (!os) {
1465
0
    return NS_ERROR_FAILURE;
1466
0
  }
1467
0
1468
0
  nsCOMPtr<nsISupports> domwin(do_QueryInterface(aWindow));
1469
0
  return os->NotifyObservers(domwin, "domwindowopened", 0);
1470
0
}
1471
1472
NS_IMETHODIMP
1473
nsWindowWatcher::RemoveWindow(mozIDOMWindowProxy* aWindow)
1474
0
{
1475
0
  // find the corresponding nsWatcherWindowEntry, remove it
1476
0
1477
0
  if (!aWindow) {
1478
0
    return NS_ERROR_INVALID_ARG;
1479
0
  }
1480
0
1481
0
  nsWatcherWindowEntry* info = FindWindowEntry(aWindow);
1482
0
  if (info) {
1483
0
    RemoveWindow(info);
1484
0
    return NS_OK;
1485
0
  }
1486
0
  NS_WARNING("requested removal of nonexistent window");
1487
0
  return NS_ERROR_INVALID_ARG;
1488
0
}
1489
1490
nsWatcherWindowEntry*
1491
nsWindowWatcher::FindWindowEntry(mozIDOMWindowProxy* aWindow)
1492
0
{
1493
0
  // find the corresponding nsWatcherWindowEntry
1494
0
  nsWatcherWindowEntry* info;
1495
0
  nsWatcherWindowEntry* listEnd;
1496
0
1497
0
  info = mOldestWindow;
1498
0
  listEnd = 0;
1499
0
  while (info != listEnd) {
1500
0
    if (info->mWindow == aWindow) {
1501
0
      return info;
1502
0
    }
1503
0
    info = info->mYounger;
1504
0
    listEnd = mOldestWindow;
1505
0
  }
1506
0
  return 0;
1507
0
}
1508
1509
nsresult
1510
nsWindowWatcher::RemoveWindow(nsWatcherWindowEntry* aInfo)
1511
0
{
1512
0
  uint32_t count = mEnumeratorList.Length();
1513
0
1514
0
  {
1515
0
    // notify the enumerators
1516
0
    MutexAutoLock lock(mListLock);
1517
0
    for (uint32_t ctr = 0; ctr < count; ++ctr) {
1518
0
      mEnumeratorList[ctr]->WindowRemoved(aInfo);
1519
0
    }
1520
0
1521
0
    // remove the element from the list
1522
0
    if (aInfo == mOldestWindow) {
1523
0
      mOldestWindow = aInfo->mYounger == mOldestWindow ? 0 : aInfo->mYounger;
1524
0
    }
1525
0
    aInfo->Unlink();
1526
0
  }
1527
0
1528
0
  // a window being removed from us signifies a newly closed window.
1529
0
  // send notifications.
1530
0
  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1531
0
  if (os) {
1532
0
    nsCOMPtr<nsISupports> domwin(do_QueryInterface(aInfo->mWindow));
1533
0
    os->NotifyObservers(domwin, "domwindowclosed", 0);
1534
0
  }
1535
0
1536
0
  delete aInfo;
1537
0
  return NS_OK;
1538
0
}
1539
1540
NS_IMETHODIMP
1541
nsWindowWatcher::GetChromeForWindow(mozIDOMWindowProxy* aWindow,
1542
                                    nsIWebBrowserChrome** aResult)
1543
0
{
1544
0
  if (!aWindow || !aResult) {
1545
0
    return NS_ERROR_INVALID_ARG;
1546
0
  }
1547
0
  *aResult = 0;
1548
0
1549
0
  MutexAutoLock lock(mListLock);
1550
0
  nsWatcherWindowEntry* info = FindWindowEntry(aWindow);
1551
0
  if (info) {
1552
0
    if (info->mChromeWeak) {
1553
0
      return info->mChromeWeak->QueryReferent(
1554
0
        NS_GET_IID(nsIWebBrowserChrome), reinterpret_cast<void**>(aResult));
1555
0
    }
1556
0
    *aResult = info->mChrome;
1557
0
    NS_IF_ADDREF(*aResult);
1558
0
  }
1559
0
  return NS_OK;
1560
0
}
1561
1562
NS_IMETHODIMP
1563
nsWindowWatcher::GetWindowByName(const nsAString& aTargetName,
1564
                                 mozIDOMWindowProxy* aCurrentWindow,
1565
                                 mozIDOMWindowProxy** aResult)
1566
0
{
1567
0
  if (!aResult) {
1568
0
    return NS_ERROR_INVALID_ARG;
1569
0
  }
1570
0
1571
0
  *aResult = nullptr;
1572
0
1573
0
  nsPIDOMWindowOuter* currentWindow =
1574
0
    aCurrentWindow ? nsPIDOMWindowOuter::From(aCurrentWindow) : nullptr;
1575
0
1576
0
  nsCOMPtr<nsIDocShellTreeItem> treeItem;
1577
0
1578
0
  nsCOMPtr<nsIDocShellTreeItem> startItem;
1579
0
  GetWindowTreeItem(currentWindow, getter_AddRefs(startItem));
1580
0
  if (startItem) {
1581
0
    // Note: original requestor is null here, per idl comments
1582
0
    startItem->FindItemWithName(aTargetName, nullptr, nullptr,
1583
0
                                /* aSkipTabGroup = */ false,
1584
0
                                getter_AddRefs(treeItem));
1585
0
  } else {
1586
0
    // Note: original requestor is null here, per idl comments
1587
0
    FindItemWithName(aTargetName, nullptr, nullptr, getter_AddRefs(treeItem));
1588
0
  }
1589
0
1590
0
  if (treeItem) {
1591
0
    nsCOMPtr<nsPIDOMWindowOuter> domWindow = treeItem->GetWindow();
1592
0
    domWindow.forget(aResult);
1593
0
  }
1594
0
1595
0
  return NS_OK;
1596
0
}
1597
1598
bool
1599
nsWindowWatcher::AddEnumerator(nsWatcherWindowEnumerator* aEnumerator)
1600
0
{
1601
0
  // (requires a lock; assumes it's called by someone holding the lock)
1602
0
  return mEnumeratorList.AppendElement(aEnumerator) != nullptr;
1603
0
}
1604
1605
bool
1606
nsWindowWatcher::RemoveEnumerator(nsWatcherWindowEnumerator* aEnumerator)
1607
0
{
1608
0
  // (requires a lock; assumes it's called by someone holding the lock)
1609
0
  return mEnumeratorList.RemoveElement(aEnumerator);
1610
0
}
1611
1612
nsresult
1613
nsWindowWatcher::URIfromURL(const char* aURL,
1614
                            mozIDOMWindowProxy* aParent,
1615
                            nsIURI** aURI)
1616
0
{
1617
0
  // Build the URI relative to the entry global.
1618
0
  nsCOMPtr<nsPIDOMWindowInner> baseWindow = do_QueryInterface(GetEntryGlobal());
1619
0
1620
0
  // failing that, build it relative to the parent window, if possible
1621
0
  if (!baseWindow && aParent) {
1622
0
    baseWindow = nsPIDOMWindowOuter::From(aParent)->GetCurrentInnerWindow();
1623
0
  }
1624
0
1625
0
  // failing that, use the given URL unmodified. It had better not be relative.
1626
0
1627
0
  nsIURI* baseURI = nullptr;
1628
0
1629
0
  // get baseWindow's document URI
1630
0
  if (baseWindow) {
1631
0
    if (nsIDocument* doc = baseWindow->GetDoc()) {
1632
0
      baseURI = doc->GetDocBaseURI();
1633
0
    }
1634
0
  }
1635
0
1636
0
  // build and return the absolute URI
1637
0
  return NS_NewURI(aURI, aURL, baseURI);
1638
0
}
1639
1640
#define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag)                       \
1641
0
  prefBranch->GetBoolPref(feature, &forceEnable);                     \
1642
0
  if (forceEnable && !aDialog && !aHasChromeParent && !aChromeURL) {  \
1643
0
    chromeFlags |= flag;                                              \
1644
0
  } else {                                                            \
1645
0
    chromeFlags |=                                                    \
1646
0
      WinHasOption(aFeatures, feature, 0, &presenceFlag) ? flag : 0;  \
1647
0
  }
1648
1649
// static
1650
uint32_t
1651
nsWindowWatcher::CalculateChromeFlagsHelper(uint32_t aInitialFlags,
1652
                                            const nsACString& aFeatures,
1653
                                            bool& presenceFlag,
1654
                                            bool aDialog,
1655
                                            bool aHasChromeParent,
1656
                                            bool aChromeURL)
1657
0
{
1658
0
  uint32_t chromeFlags = aInitialFlags;
1659
0
1660
0
  nsresult rv;
1661
0
  nsCOMPtr<nsIPrefBranch> prefBranch;
1662
0
  nsCOMPtr<nsIPrefService> prefs =
1663
0
    do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
1664
0
1665
0
  NS_ENSURE_SUCCESS(rv, nsIWebBrowserChrome::CHROME_DEFAULT);
1666
0
1667
0
  rv = prefs->GetBranch("dom.disable_window_open_feature.",
1668
0
                        getter_AddRefs(prefBranch));
1669
0
1670
0
  NS_ENSURE_SUCCESS(rv, nsIWebBrowserChrome::CHROME_DEFAULT);
1671
0
1672
0
  // NS_CALCULATE_CHROME_FLAG_FOR requires aFeatures, forceEnable, aDialog
1673
0
  // aHasChromeParent, aChromeURL, presenceFlag and chromeFlags to be in
1674
0
  // scope.
1675
0
  bool forceEnable = false;
1676
0
1677
0
  NS_CALCULATE_CHROME_FLAG_FOR("titlebar",
1678
0
                               nsIWebBrowserChrome::CHROME_TITLEBAR);
1679
0
  NS_CALCULATE_CHROME_FLAG_FOR("close",
1680
0
                               nsIWebBrowserChrome::CHROME_WINDOW_CLOSE);
1681
0
  NS_CALCULATE_CHROME_FLAG_FOR("toolbar",
1682
0
                               nsIWebBrowserChrome::CHROME_TOOLBAR);
1683
0
  NS_CALCULATE_CHROME_FLAG_FOR("location",
1684
0
                               nsIWebBrowserChrome::CHROME_LOCATIONBAR);
1685
0
  NS_CALCULATE_CHROME_FLAG_FOR("personalbar",
1686
0
                               nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
1687
0
  NS_CALCULATE_CHROME_FLAG_FOR("status",
1688
0
                               nsIWebBrowserChrome::CHROME_STATUSBAR);
1689
0
  NS_CALCULATE_CHROME_FLAG_FOR("menubar",
1690
0
                               nsIWebBrowserChrome::CHROME_MENUBAR);
1691
0
  NS_CALCULATE_CHROME_FLAG_FOR("resizable",
1692
0
                               nsIWebBrowserChrome::CHROME_WINDOW_RESIZE);
1693
0
  NS_CALCULATE_CHROME_FLAG_FOR("minimizable",
1694
0
                               nsIWebBrowserChrome::CHROME_WINDOW_MIN);
1695
0
1696
0
  // default scrollbar to "on," unless explicitly turned off
1697
0
  bool scrollbarsPresent = false;
1698
0
  if (WinHasOption(aFeatures, "scrollbars", 1, &scrollbarsPresent) ||
1699
0
      !scrollbarsPresent) {
1700
0
    chromeFlags |= nsIWebBrowserChrome::CHROME_SCROLLBARS;
1701
0
  }
1702
0
  presenceFlag = presenceFlag || scrollbarsPresent;
1703
0
1704
0
  return chromeFlags;
1705
0
}
1706
1707
// static
1708
uint32_t
1709
nsWindowWatcher::EnsureFlagsSafeForContent(uint32_t aChromeFlags,
1710
                                           bool aChromeURL)
1711
0
{
1712
0
  aChromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
1713
0
  aChromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
1714
0
  aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
1715
0
  aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
1716
0
  aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP;
1717
0
  /* Untrusted script is allowed to pose modal windows with a chrome
1718
0
     scheme. This check could stand to be better. But it effectively
1719
0
     prevents untrusted script from opening modal windows in general
1720
0
     while still allowing alerts and the like. */
1721
0
  if (!aChromeURL) {
1722
0
    aChromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL |
1723
0
                     nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
1724
0
  }
1725
0
1726
0
  if (!(aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)) {
1727
0
    aChromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT;
1728
0
  }
1729
0
1730
0
  return aChromeFlags;
1731
0
}
1732
1733
/**
1734
 * Calculate the chrome bitmask from a string list of features requested
1735
 * from a child process. Feature strings that are restricted to the parent
1736
 * process are ignored here.
1737
 * @param aFeatures a string containing a list of named features
1738
 * @return the chrome bitmask
1739
 */
1740
// static
1741
uint32_t
1742
nsWindowWatcher::CalculateChromeFlagsForChild(const nsACString& aFeatures)
1743
0
{
1744
0
  if (aFeatures.IsVoid()) {
1745
0
    return nsIWebBrowserChrome::CHROME_ALL;
1746
0
  }
1747
0
1748
0
  bool presenceFlag = false;
1749
0
  uint32_t chromeFlags = CalculateChromeFlagsHelper(
1750
0
    nsIWebBrowserChrome::CHROME_WINDOW_BORDERS, aFeatures, presenceFlag);
1751
0
1752
0
  return EnsureFlagsSafeForContent(chromeFlags);
1753
0
}
1754
1755
/**
1756
 * Calculate the chrome bitmask from a string list of features for a new
1757
 * privileged window.
1758
 * @param aParent the opener window
1759
 * @param aFeatures a string containing a list of named chrome features
1760
 * @param aDialog affects the assumptions made about unnamed features
1761
 * @param aChromeURL true if the window is being sent to a chrome:// URL
1762
 * @param aHasChromeParent true if the parent window is privileged
1763
 * @param aCalledFromJS true if the window open request came from script.
1764
 * @return the chrome bitmask
1765
 */
1766
// static
1767
uint32_t
1768
nsWindowWatcher::CalculateChromeFlagsForParent(mozIDOMWindowProxy* aParent,
1769
                                               const nsACString& aFeatures,
1770
                                               bool aDialog,
1771
                                               bool aChromeURL,
1772
                                               bool aHasChromeParent,
1773
                                               bool aCalledFromJS)
1774
0
{
1775
0
  MOZ_ASSERT(XRE_IsParentProcess());
1776
0
  MOZ_ASSERT(nsContentUtils::LegacyIsCallerChromeOrNativeCode());
1777
0
1778
0
  uint32_t chromeFlags = 0;
1779
0
1780
0
  // The features string is made void by OpenWindowInternal
1781
0
  // if nullptr was originally passed as the features string.
1782
0
  if (aFeatures.IsVoid()) {
1783
0
    chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
1784
0
    if (aDialog) {
1785
0
      chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
1786
0
                     nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
1787
0
    }
1788
0
  } else {
1789
0
    chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
1790
0
  }
1791
0
1792
0
  /* This function has become complicated since browser windows and
1793
0
     dialogs diverged. The difference is, browser windows assume all
1794
0
     chrome not explicitly mentioned is off, if the features string
1795
0
     is not null. Exceptions are some OS border chrome new with Mozilla.
1796
0
     Dialogs interpret a (mostly) empty features string to mean
1797
0
     "OS's choice," and also support an "all" flag explicitly disallowed
1798
0
     in the standards-compliant window.(normal)open. */
1799
0
1800
0
  bool presenceFlag = false;
1801
0
  if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag)) {
1802
0
    chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
1803
0
  }
1804
0
1805
0
  /* Next, allow explicitly named options to override the initial settings */
1806
0
  chromeFlags = CalculateChromeFlagsHelper(chromeFlags, aFeatures, presenceFlag,
1807
0
                                           aDialog, aHasChromeParent, aChromeURL);
1808
0
1809
0
  // Determine whether the window is a private browsing window
1810
0
  chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ?
1811
0
    nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0;
1812
0
  chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag) ?
1813
0
    nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW : 0;
1814
0
1815
0
  // Determine whether the window should have remote tabs.
1816
0
  bool remote = BrowserTabsRemoteAutostart();
1817
0
1818
0
  if (remote) {
1819
0
    remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag);
1820
0
  } else {
1821
0
    remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag);
1822
0
  }
1823
0
1824
0
  if (remote) {
1825
0
    chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
1826
0
  }
1827
0
1828
0
  chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag) ?
1829
0
    nsIWebBrowserChrome::CHROME_WINDOW_POPUP : 0;
1830
0
1831
0
  /* OK.
1832
0
     Normal browser windows, in spite of a stated pattern of turning off
1833
0
     all chrome not mentioned explicitly, will want the new OS chrome (window
1834
0
     borders, titlebars, closebox) on, unless explicitly turned off.
1835
0
     Dialogs, on the other hand, take the absence of any explicit settings
1836
0
     to mean "OS' choice." */
1837
0
1838
0
  // default titlebar and closebox to "on," if not mentioned at all
1839
0
  if (!(chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)) {
1840
0
    if (!PL_strcasestr(aFeatures.BeginReading(), "titlebar")) {
1841
0
      chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
1842
0
    }
1843
0
    if (!PL_strcasestr(aFeatures.BeginReading(), "close")) {
1844
0
      chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
1845
0
    }
1846
0
  }
1847
0
1848
0
  if (aDialog && !aFeatures.IsVoid() && !presenceFlag) {
1849
0
    chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT;
1850
0
  }
1851
0
1852
0
  /* Finally, once all the above normal chrome has been divined, deal
1853
0
     with the features that are more operating hints than appearance
1854
0
     instructions. (Note modality implies dependence.) */
1855
0
1856
0
  if (WinHasOption(aFeatures, "alwaysLowered", 0, nullptr) ||
1857
0
      WinHasOption(aFeatures, "z-lock", 0, nullptr)) {
1858
0
    chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
1859
0
  } else if (WinHasOption(aFeatures, "alwaysRaised", 0, nullptr)) {
1860
0
    chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
1861
0
  }
1862
0
1863
0
  chromeFlags |= WinHasOption(aFeatures, "suppressanimation", 0, nullptr) ?
1864
0
    nsIWebBrowserChrome::CHROME_SUPPRESS_ANIMATION : 0;
1865
0
1866
0
  chromeFlags |= WinHasOption(aFeatures, "chrome", 0, nullptr) ?
1867
0
    nsIWebBrowserChrome::CHROME_OPENAS_CHROME : 0;
1868
0
  chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nullptr) ?
1869
0
    nsIWebBrowserChrome::CHROME_EXTRA : 0;
1870
0
  chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nullptr) ?
1871
0
    nsIWebBrowserChrome::CHROME_CENTER_SCREEN : 0;
1872
0
  chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nullptr) ?
1873
0
    nsIWebBrowserChrome::CHROME_DEPENDENT : 0;
1874
0
  chromeFlags |= WinHasOption(aFeatures, "modal", 0, nullptr) ?
1875
0
    (nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT) : 0;
1876
0
1877
0
  /* On mobile we want to ignore the dialog window feature, since the mobile UI
1878
0
     does not provide any affordance for dialog windows. This does not interfere
1879
0
     with dialog windows created through openDialog. */
1880
0
  bool disableDialogFeature = false;
1881
0
  nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
1882
0
1883
0
  branch->GetBoolPref("dom.disable_window_open_dialog_feature",
1884
0
                      &disableDialogFeature);
1885
0
1886
0
  if (!disableDialogFeature) {
1887
0
    chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr) ?
1888
0
      nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0;
1889
0
  }
1890
0
1891
0
  /* and dialogs need to have the last word. assume dialogs are dialogs,
1892
0
     and opened as chrome, unless explicitly told otherwise. */
1893
0
  if (aDialog) {
1894
0
    if (!PL_strcasestr(aFeatures.BeginReading(), "dialog")) {
1895
0
      chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
1896
0
    }
1897
0
    if (!PL_strcasestr(aFeatures.BeginReading(), "chrome")) {
1898
0
      chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
1899
0
    }
1900
0
  }
1901
0
1902
0
  /* missing
1903
0
     chromeFlags->copy_history
1904
0
   */
1905
0
1906
0
  // Check security state for use in determing window dimensions
1907
0
  if (!aHasChromeParent) {
1908
0
    chromeFlags = EnsureFlagsSafeForContent(chromeFlags, aChromeURL);
1909
0
  }
1910
0
1911
0
  // Disable CHROME_OPENAS_DIALOG if the window is inside <iframe mozbrowser>.
1912
0
  // It's up to the embedder to interpret what dialog=1 means.
1913
0
  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
1914
0
  if (docshell && docshell->GetIsInMozBrowser()) {
1915
0
    chromeFlags &= ~nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
1916
0
  }
1917
0
1918
0
  return chromeFlags;
1919
0
}
1920
1921
// static
1922
int32_t
1923
nsWindowWatcher::WinHasOption(const nsACString& aOptions, const char* aName,
1924
                              int32_t aDefault, bool* aPresenceFlag)
1925
0
{
1926
0
  if (aOptions.IsEmpty()) {
1927
0
    return 0;
1928
0
  }
1929
0
1930
0
  const char* options = aOptions.BeginReading();
1931
0
  char* comma;
1932
0
  char* equal;
1933
0
  int32_t found = 0;
1934
0
1935
#ifdef DEBUG
1936
  NS_ASSERTION(nsAutoCString(aOptions).FindCharInSet(" \n\r\t") == kNotFound,
1937
               "There should be no whitespace in this string!");
1938
#endif
1939
1940
0
  while (true) {
1941
0
    comma = PL_strchr(options, ',');
1942
0
    if (comma) {
1943
0
      *comma = '\0';
1944
0
    }
1945
0
    equal = PL_strchr(options, '=');
1946
0
    if (equal) {
1947
0
      *equal = '\0';
1948
0
    }
1949
0
    if (nsCRT::strcasecmp(options, aName) == 0) {
1950
0
      if (aPresenceFlag) {
1951
0
        *aPresenceFlag = true;
1952
0
      }
1953
0
      if (equal)
1954
0
        if (*(equal + 1) == '*') {
1955
0
          found = aDefault;
1956
0
        } else if (nsCRT::strcasecmp(equal + 1, "yes") == 0) {
1957
0
          found = 1;
1958
0
        } else {
1959
0
          found = atoi(equal + 1);
1960
0
        }
1961
0
      else {
1962
0
        found = 1;
1963
0
      }
1964
0
    }
1965
0
    if (equal) {
1966
0
      *equal = '=';
1967
0
    }
1968
0
    if (comma) {
1969
0
      *comma = ',';
1970
0
    }
1971
0
    if (found || !comma) {
1972
0
      break;
1973
0
    }
1974
0
    options = comma + 1;
1975
0
  }
1976
0
  return found;
1977
0
}
1978
1979
/* try to find an nsIDocShellTreeItem with the given name in any
1980
   known open window. a failure to find the item will not
1981
   necessarily return a failure method value. check aFoundItem.
1982
*/
1983
NS_IMETHODIMP
1984
nsWindowWatcher::FindItemWithName(const nsAString& aName,
1985
                                  nsIDocShellTreeItem* aRequestor,
1986
                                  nsIDocShellTreeItem* aOriginalRequestor,
1987
                                  nsIDocShellTreeItem** aFoundItem)
1988
0
{
1989
0
  *aFoundItem = nullptr;
1990
0
  if (aName.IsEmpty()) {
1991
0
    return NS_OK;
1992
0
  }
1993
0
1994
0
  if (aName.LowerCaseEqualsLiteral("_blank") ||
1995
0
      aName.LowerCaseEqualsLiteral("_top") ||
1996
0
      aName.LowerCaseEqualsLiteral("_parent") ||
1997
0
      aName.LowerCaseEqualsLiteral("_self")) {
1998
0
    return NS_OK;
1999
0
  }
2000
0
2001
0
  // If we are looking for an item and we don't have a docshell we are checking
2002
0
  // on, let's just look in the chrome tab group!
2003
0
  return TabGroup::GetChromeTabGroup()->FindItemWithName(aName,
2004
0
                                                         aRequestor,
2005
0
                                                         aOriginalRequestor,
2006
0
                                                         aFoundItem);
2007
0
}
2008
2009
already_AddRefed<nsIDocShellTreeItem>
2010
nsWindowWatcher::GetCallerTreeItem(nsIDocShellTreeItem* aParentItem)
2011
0
{
2012
0
  nsCOMPtr<nsIWebNavigation> callerWebNav = do_GetInterface(GetEntryGlobal());
2013
0
  nsCOMPtr<nsIDocShellTreeItem> callerItem = do_QueryInterface(callerWebNav);
2014
0
  if (!callerItem) {
2015
0
    callerItem = aParentItem;
2016
0
  }
2017
0
2018
0
  return callerItem.forget();
2019
0
}
2020
2021
nsPIDOMWindowOuter*
2022
nsWindowWatcher::SafeGetWindowByName(const nsAString& aName,
2023
                                     bool aForceNoOpener,
2024
                                     mozIDOMWindowProxy* aCurrentWindow)
2025
0
{
2026
0
  if (aForceNoOpener) {
2027
0
    if (!aName.LowerCaseEqualsLiteral("_self") &&
2028
0
        !aName.LowerCaseEqualsLiteral("_top") &&
2029
0
        !aName.LowerCaseEqualsLiteral("_parent")) {
2030
0
      // Ignore all other names in the noopener case.
2031
0
      return nullptr;
2032
0
    }
2033
0
  }
2034
0
2035
0
  nsCOMPtr<nsIDocShellTreeItem> startItem;
2036
0
  GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
2037
0
2038
0
  nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(startItem);
2039
0
2040
0
  nsCOMPtr<nsIDocShellTreeItem> foundItem;
2041
0
  if (startItem) {
2042
0
    startItem->FindItemWithName(aName, nullptr, callerItem,
2043
0
                                /* aSkipTabGroup = */ false,
2044
0
                                getter_AddRefs(foundItem));
2045
0
  } else {
2046
0
    FindItemWithName(aName, nullptr, callerItem,
2047
0
                     getter_AddRefs(foundItem));
2048
0
  }
2049
0
2050
0
  return foundItem ? foundItem->GetWindow() : nullptr;
2051
0
}
2052
2053
/* Fetch the nsIDOMWindow corresponding to the given nsIDocShellTreeItem.
2054
   This forces the creation of a script context, if one has not already
2055
   been created. Note it also sets the window's opener to the parent,
2056
   if applicable -- because it's just convenient, that's all. null aParent
2057
   is acceptable. */
2058
nsresult
2059
nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem* aOpenedItem,
2060
                                         nsPIDOMWindowOuter* aParent,
2061
                                         bool aWindowIsNew,
2062
                                         bool aForceNoOpener,
2063
                                         mozIDOMWindowProxy** aOpenedWindow)
2064
0
{
2065
0
  nsresult rv = NS_ERROR_FAILURE;
2066
0
2067
0
  NS_ENSURE_ARG(aOpenedWindow);
2068
0
2069
0
  *aOpenedWindow = 0;
2070
0
  nsCOMPtr<nsPIDOMWindowOuter> piOpenedWindow = aOpenedItem->GetWindow();
2071
0
  if (piOpenedWindow) {
2072
0
    if (!aForceNoOpener) {
2073
0
      piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit
2074
0
    } else if (aParent && aParent != piOpenedWindow) {
2075
0
      MOZ_ASSERT(piOpenedWindow->TabGroup() != aParent->TabGroup(),
2076
0
                 "If we're forcing no opener, they should be in different tab groups");
2077
0
    }
2078
0
2079
0
    if (aWindowIsNew) {
2080
#ifdef DEBUG
2081
      // Assert that we're not loading things right now.  If we are, when
2082
      // that load completes it will clobber whatever principals we set up
2083
      // on this new window!
2084
      nsCOMPtr<nsIDocumentLoader> docloader = do_QueryInterface(aOpenedItem);
2085
      NS_ASSERTION(docloader, "How can we not have a docloader here?");
2086
2087
      nsCOMPtr<nsIChannel> chan;
2088
      docloader->GetDocumentChannel(getter_AddRefs(chan));
2089
      NS_ASSERTION(!chan, "Why is there a document channel?");
2090
#endif
2091
2092
0
      nsCOMPtr<nsIDocument> doc = piOpenedWindow->GetExtantDoc();
2093
0
      if (doc) {
2094
0
        doc->SetIsInitialDocument(true);
2095
0
      }
2096
0
    }
2097
0
    rv = CallQueryInterface(piOpenedWindow, aOpenedWindow);
2098
0
  }
2099
0
  return rv;
2100
0
}
2101
2102
// static
2103
void
2104
nsWindowWatcher::CalcSizeSpec(const nsACString& aFeatures, SizeSpec& aResult)
2105
0
{
2106
0
  // Parse position spec, if any, from aFeatures
2107
0
  bool present;
2108
0
  int32_t temp;
2109
0
2110
0
  present = false;
2111
0
  if ((temp = WinHasOption(aFeatures, "left", 0, &present)) || present) {
2112
0
    aResult.mLeft = temp;
2113
0
  } else if ((temp = WinHasOption(aFeatures, "screenX", 0, &present)) ||
2114
0
             present) {
2115
0
    aResult.mLeft = temp;
2116
0
  }
2117
0
  aResult.mLeftSpecified = present;
2118
0
2119
0
  present = false;
2120
0
  if ((temp = WinHasOption(aFeatures, "top", 0, &present)) || present) {
2121
0
    aResult.mTop = temp;
2122
0
  } else if ((temp = WinHasOption(aFeatures, "screenY", 0, &present)) ||
2123
0
             present) {
2124
0
    aResult.mTop = temp;
2125
0
  }
2126
0
  aResult.mTopSpecified = present;
2127
0
2128
0
  // Parse size spec, if any. Chrome size overrides content size.
2129
0
  if ((temp = WinHasOption(aFeatures, "outerWidth", INT32_MIN, nullptr))) {
2130
0
    if (temp == INT32_MIN) {
2131
0
      aResult.mUseDefaultWidth = true;
2132
0
    } else {
2133
0
      aResult.mOuterWidth = temp;
2134
0
    }
2135
0
    aResult.mOuterWidthSpecified = true;
2136
0
  } else if ((temp = WinHasOption(aFeatures, "width", INT32_MIN, nullptr)) ||
2137
0
             (temp = WinHasOption(aFeatures, "innerWidth", INT32_MIN,
2138
0
                                  nullptr))) {
2139
0
    if (temp == INT32_MIN) {
2140
0
      aResult.mUseDefaultWidth = true;
2141
0
    } else {
2142
0
      aResult.mInnerWidth = temp;
2143
0
    }
2144
0
    aResult.mInnerWidthSpecified = true;
2145
0
  }
2146
0
2147
0
  if ((temp = WinHasOption(aFeatures, "outerHeight", INT32_MIN, nullptr))) {
2148
0
    if (temp == INT32_MIN) {
2149
0
      aResult.mUseDefaultHeight = true;
2150
0
    } else {
2151
0
      aResult.mOuterHeight = temp;
2152
0
    }
2153
0
    aResult.mOuterHeightSpecified = true;
2154
0
  } else if ((temp = WinHasOption(aFeatures, "height", INT32_MIN,
2155
0
                                  nullptr)) ||
2156
0
             (temp = WinHasOption(aFeatures, "innerHeight", INT32_MIN,
2157
0
                                  nullptr))) {
2158
0
    if (temp == INT32_MIN) {
2159
0
      aResult.mUseDefaultHeight = true;
2160
0
    } else {
2161
0
      aResult.mInnerHeight = temp;
2162
0
    }
2163
0
    aResult.mInnerHeightSpecified = true;
2164
0
  }
2165
0
}
2166
2167
/* Size and position a new window according to aSizeSpec. This method
2168
   is assumed to be called after the window has already been given
2169
   a default position and size; thus its current position and size are
2170
   accurate defaults. The new window is made visible at method end.
2171
   @param aTreeOwner
2172
          The top-level nsIDocShellTreeOwner of the newly opened window.
2173
   @param aParent (optional)
2174
          The parent window from which to inherit zoom factors from if
2175
          aOpenerFullZoom is none.
2176
   @param aIsCallerChrome
2177
          True if the code requesting the new window is privileged.
2178
   @param aSizeSpec
2179
          The size that the new window should be.
2180
   @param aOpenerFullZoom
2181
          If not nothing, a zoom factor to scale the content to.
2182
*/
2183
void
2184
nsWindowWatcher::SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner,
2185
                                  mozIDOMWindowProxy* aParent,
2186
                                  bool aIsCallerChrome,
2187
                                  const SizeSpec& aSizeSpec,
2188
                                  const Maybe<float>& aOpenerFullZoom)
2189
0
{
2190
0
  // We should only be sizing top-level windows if we're in the parent
2191
0
  // process.
2192
0
  MOZ_ASSERT(XRE_IsParentProcess());
2193
0
2194
0
  // position and size of window
2195
0
  int32_t left = 0, top = 0, width = 100, height = 100;
2196
0
  // difference between chrome and content size
2197
0
  int32_t chromeWidth = 0, chromeHeight = 0;
2198
0
  // whether the window size spec refers to chrome or content
2199
0
  bool sizeChromeWidth = true, sizeChromeHeight = true;
2200
0
2201
0
  // get various interfaces for aDocShellItem, used throughout this method
2202
0
  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(aTreeOwner));
2203
0
  if (!treeOwnerAsWin) { // we'll need this to actually size the docshell
2204
0
    return;
2205
0
  }
2206
0
2207
0
  double openerZoom = aOpenerFullZoom.valueOr(1.0);
2208
0
  if (aParent && aOpenerFullZoom.isNothing()) {
2209
0
    nsCOMPtr<nsPIDOMWindowOuter> piWindow = nsPIDOMWindowOuter::From(aParent);
2210
0
    if (nsIDocument* doc = piWindow->GetDoc()) {
2211
0
      if (nsPresContext* presContext = doc->GetPresContext()) {
2212
0
        openerZoom = presContext->GetFullZoom();
2213
0
      }
2214
0
    }
2215
0
  }
2216
0
2217
0
  double scale = 1.0;
2218
0
  treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
2219
0
2220
0
  /* The current position and size will be unchanged if not specified
2221
0
     (and they fit entirely onscreen). Also, calculate the difference
2222
0
     between chrome and content sizes on aDocShellItem's window.
2223
0
     This latter point becomes important if chrome and content
2224
0
     specifications are mixed in aFeatures, and when bringing the window
2225
0
     back from too far off the right or bottom edges of the screen. */
2226
0
2227
0
  treeOwnerAsWin->GetPositionAndSize(&left, &top, &width, &height);
2228
0
  left = NSToIntRound(left / scale);
2229
0
  top = NSToIntRound(top / scale);
2230
0
  width = NSToIntRound(width / scale);
2231
0
  height = NSToIntRound(height / scale);
2232
0
  {
2233
0
    int32_t contentWidth, contentHeight;
2234
0
    bool hasPrimaryContent = false;
2235
0
    aTreeOwner->GetHasPrimaryContent(&hasPrimaryContent);
2236
0
    if (hasPrimaryContent) {
2237
0
      aTreeOwner->GetPrimaryContentSize(&contentWidth, &contentHeight);
2238
0
    } else {
2239
0
      aTreeOwner->GetRootShellSize(&contentWidth, &contentHeight);
2240
0
    }
2241
0
    chromeWidth = width - contentWidth;
2242
0
    chromeHeight = height - contentHeight;
2243
0
  }
2244
0
2245
0
  // Set up left/top
2246
0
  if (aSizeSpec.mLeftSpecified) {
2247
0
    left = NSToIntRound(aSizeSpec.mLeft * openerZoom);
2248
0
  }
2249
0
2250
0
  if (aSizeSpec.mTopSpecified) {
2251
0
    top = NSToIntRound(aSizeSpec.mTop * openerZoom);
2252
0
  }
2253
0
2254
0
  // Set up width
2255
0
  if (aSizeSpec.mOuterWidthSpecified) {
2256
0
    if (!aSizeSpec.mUseDefaultWidth) {
2257
0
      width = NSToIntRound(aSizeSpec.mOuterWidth * openerZoom);
2258
0
    } // Else specified to default; just use our existing width
2259
0
  } else if (aSizeSpec.mInnerWidthSpecified) {
2260
0
    sizeChromeWidth = false;
2261
0
    if (aSizeSpec.mUseDefaultWidth) {
2262
0
      width = width - chromeWidth;
2263
0
    } else {
2264
0
      width = NSToIntRound(aSizeSpec.mInnerWidth * openerZoom);
2265
0
    }
2266
0
  }
2267
0
2268
0
  // Set up height
2269
0
  if (aSizeSpec.mOuterHeightSpecified) {
2270
0
    if (!aSizeSpec.mUseDefaultHeight) {
2271
0
      height = NSToIntRound(aSizeSpec.mOuterHeight * openerZoom);
2272
0
    } // Else specified to default; just use our existing height
2273
0
  } else if (aSizeSpec.mInnerHeightSpecified) {
2274
0
    sizeChromeHeight = false;
2275
0
    if (aSizeSpec.mUseDefaultHeight) {
2276
0
      height = height - chromeHeight;
2277
0
    } else {
2278
0
      height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom);
2279
0
    }
2280
0
  }
2281
0
2282
0
  bool positionSpecified = aSizeSpec.PositionSpecified();
2283
0
2284
0
  // Check security state for use in determing window dimensions
2285
0
  bool enabled = false;
2286
0
  if (aIsCallerChrome) {
2287
0
    // Only enable special priveleges for chrome when chrome calls
2288
0
    // open() on a chrome window
2289
0
    nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(aParent));
2290
0
    enabled = !aParent || chromeWin;
2291
0
  }
2292
0
2293
0
  if (!enabled) {
2294
0
    // Security check failed.  Ensure all args meet minimum reqs.
2295
0
2296
0
    int32_t oldTop = top, oldLeft = left;
2297
0
2298
0
    // We'll also need the screen dimensions
2299
0
    nsCOMPtr<nsIScreen> screen;
2300
0
    nsCOMPtr<nsIScreenManager> screenMgr(
2301
0
      do_GetService("@mozilla.org/gfx/screenmanager;1"));
2302
0
    if (screenMgr)
2303
0
      screenMgr->ScreenForRect(left, top, width, height,
2304
0
                               getter_AddRefs(screen));
2305
0
    if (screen) {
2306
0
      int32_t screenLeft, screenTop, screenWidth, screenHeight;
2307
0
      int32_t winWidth = width + (sizeChromeWidth ? 0 : chromeWidth),
2308
0
              winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
2309
0
2310
0
      // Get screen dimensions (in device pixels)
2311
0
      screen->GetAvailRect(&screenLeft, &screenTop, &screenWidth,
2312
0
                           &screenHeight);
2313
0
      // Convert them to CSS pixels
2314
0
      screenLeft = NSToIntRound(screenLeft / scale);
2315
0
      screenTop = NSToIntRound(screenTop / scale);
2316
0
      screenWidth = NSToIntRound(screenWidth / scale);
2317
0
      screenHeight = NSToIntRound(screenHeight / scale);
2318
0
2319
0
      if (aSizeSpec.SizeSpecified()) {
2320
0
        if (!nsContentUtils::ShouldResistFingerprinting()) {
2321
0
          /* Unlike position, force size out-of-bounds check only if
2322
0
             size actually was specified. Otherwise, intrinsically sized
2323
0
             windows are broken. */
2324
0
          if (height < 100) {
2325
0
            height = 100;
2326
0
            winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
2327
0
          }
2328
0
          if (winHeight > screenHeight) {
2329
0
            height = screenHeight - (sizeChromeHeight ? 0 : chromeHeight);
2330
0
          }
2331
0
          if (width < 100) {
2332
0
            width = 100;
2333
0
            winWidth = width + (sizeChromeWidth ? 0 : chromeWidth);
2334
0
          }
2335
0
          if (winWidth > screenWidth) {
2336
0
            width = screenWidth - (sizeChromeWidth ? 0 : chromeWidth);
2337
0
          }
2338
0
        } else {
2339
0
          int32_t targetContentWidth  = 0;
2340
0
          int32_t targetContentHeight = 0;
2341
0
2342
0
          nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
2343
0
            chromeWidth,
2344
0
            chromeHeight,
2345
0
            screenWidth,
2346
0
            screenHeight,
2347
0
            width,
2348
0
            height,
2349
0
            sizeChromeWidth,
2350
0
            sizeChromeHeight,
2351
0
            &targetContentWidth,
2352
0
            &targetContentHeight
2353
0
          );
2354
0
2355
0
          if (aSizeSpec.mInnerWidthSpecified ||
2356
0
              aSizeSpec.mOuterWidthSpecified) {
2357
0
            width = targetContentWidth;
2358
0
            winWidth = width + (sizeChromeWidth ? 0 : chromeWidth);
2359
0
          }
2360
0
2361
0
          if (aSizeSpec.mInnerHeightSpecified ||
2362
0
              aSizeSpec.mOuterHeightSpecified) {
2363
0
            height = targetContentHeight;
2364
0
            winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
2365
0
          }
2366
0
        }
2367
0
      }
2368
0
2369
0
      CheckedInt<decltype(left)> leftPlusWinWidth = left;
2370
0
      leftPlusWinWidth += winWidth;
2371
0
      if (!leftPlusWinWidth.isValid() ||
2372
0
          leftPlusWinWidth.value() > screenLeft + screenWidth) {
2373
0
        left = screenLeft + screenWidth - winWidth;
2374
0
      }
2375
0
      if (left < screenLeft) {
2376
0
        left = screenLeft;
2377
0
      }
2378
0
2379
0
      CheckedInt<decltype(top)> topPlusWinHeight = top;
2380
0
      topPlusWinHeight += winHeight;
2381
0
      if (!topPlusWinHeight.isValid() ||
2382
0
          topPlusWinHeight.value() > screenTop + screenHeight) {
2383
0
        top = screenTop + screenHeight - winHeight;
2384
0
      }
2385
0
      if (top < screenTop) {
2386
0
        top = screenTop;
2387
0
      }
2388
0
2389
0
      if (top != oldTop || left != oldLeft) {
2390
0
        positionSpecified = true;
2391
0
      }
2392
0
    }
2393
0
  }
2394
0
2395
0
  // size and position the window
2396
0
2397
0
  if (positionSpecified) {
2398
0
    // Get the scale factor appropriate for the screen we're actually
2399
0
    // positioning on.
2400
0
    nsCOMPtr<nsIScreen> screen;
2401
0
    nsCOMPtr<nsIScreenManager> screenMgr(
2402
0
      do_GetService("@mozilla.org/gfx/screenmanager;1"));
2403
0
    if (screenMgr) {
2404
0
      screenMgr->ScreenForRect(left, top, 1, 1, getter_AddRefs(screen));
2405
0
    }
2406
0
    if (screen) {
2407
0
      double cssToDevPixScale, desktopToDevPixScale;
2408
0
      screen->GetDefaultCSSScaleFactor(&cssToDevPixScale);
2409
0
      screen->GetContentsScaleFactor(&desktopToDevPixScale);
2410
0
      double cssToDesktopScale = cssToDevPixScale / desktopToDevPixScale;
2411
0
      int32_t screenLeft, screenTop, screenWd, screenHt;
2412
0
      screen->GetRectDisplayPix(&screenLeft, &screenTop, &screenWd, &screenHt);
2413
0
      // Adjust by desktop-pixel origin of the target screen when scaling
2414
0
      // to convert from per-screen CSS-px coords to global desktop coords.
2415
0
      treeOwnerAsWin->SetPositionDesktopPix(
2416
0
        (left - screenLeft) * cssToDesktopScale + screenLeft,
2417
0
        (top - screenTop) * cssToDesktopScale + screenTop);
2418
0
    } else {
2419
0
      // Couldn't find screen? This shouldn't happen.
2420
0
      treeOwnerAsWin->SetPosition(left * scale, top * scale);
2421
0
    }
2422
0
    // This shouldn't be necessary, given the screen check above, but in case
2423
0
    // moving the window didn't put it where we expected (e.g. due to issues
2424
0
    // at the widget level, or whatever), let's re-fetch the scale factor for
2425
0
    // wherever it really ended up
2426
0
    treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
2427
0
  }
2428
0
  if (aSizeSpec.SizeSpecified()) {
2429
0
    /* Prefer to trust the interfaces, which think in terms of pure
2430
0
       chrome or content sizes. If we have a mix, use the chrome size
2431
0
       adjusted by the chrome/content differences calculated earlier. */
2432
0
    if (!sizeChromeWidth && !sizeChromeHeight) {
2433
0
      bool hasPrimaryContent = false;
2434
0
      aTreeOwner->GetHasPrimaryContent(&hasPrimaryContent);
2435
0
      if (hasPrimaryContent) {
2436
0
        aTreeOwner->SetPrimaryContentSize(width * scale, height * scale);
2437
0
      } else {
2438
0
        aTreeOwner->SetRootShellSize(width * scale, height * scale);
2439
0
      }
2440
0
    } else {
2441
0
      if (!sizeChromeWidth) {
2442
0
        width += chromeWidth;
2443
0
      }
2444
0
      if (!sizeChromeHeight) {
2445
0
        height += chromeHeight;
2446
0
      }
2447
0
      treeOwnerAsWin->SetSize(width * scale, height * scale, false);
2448
0
    }
2449
0
  }
2450
0
  treeOwnerAsWin->SetVisibility(true);
2451
0
}
2452
2453
void
2454
nsWindowWatcher::GetWindowTreeItem(mozIDOMWindowProxy* aWindow,
2455
                                   nsIDocShellTreeItem** aResult)
2456
0
{
2457
0
  *aResult = 0;
2458
0
2459
0
  if (aWindow) {
2460
0
    nsIDocShell* docshell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell();
2461
0
    if (docshell) {
2462
0
      CallQueryInterface(docshell, aResult);
2463
0
    }
2464
0
  }
2465
0
}
2466
2467
void
2468
nsWindowWatcher::GetWindowTreeOwner(nsPIDOMWindowOuter* aWindow,
2469
                                    nsIDocShellTreeOwner** aResult)
2470
0
{
2471
0
  *aResult = 0;
2472
0
2473
0
  nsCOMPtr<nsIDocShellTreeItem> treeItem;
2474
0
  GetWindowTreeItem(aWindow, getter_AddRefs(treeItem));
2475
0
  if (treeItem) {
2476
0
    treeItem->GetTreeOwner(aResult);
2477
0
  }
2478
0
}
2479
2480
/* static */
2481
int32_t
2482
nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter* aParent,
2483
                                       uint32_t aChromeFlags,
2484
                                       bool aCalledFromJS,
2485
                                       bool aPositionSpecified,
2486
                                       bool aSizeSpecified)
2487
0
{
2488
0
  bool isFullScreen = aParent->GetFullScreen();
2489
0
2490
0
  // Where should we open this?
2491
0
  int32_t containerPref;
2492
0
  if (NS_FAILED(Preferences::GetInt("browser.link.open_newwindow",
2493
0
                                    &containerPref))) {
2494
0
    // We couldn't read the user preference, so fall back on the default.
2495
0
    return nsIBrowserDOMWindow::OPEN_NEWTAB;
2496
0
  }
2497
0
2498
0
  bool isDisabledOpenNewWindow =
2499
0
    isFullScreen &&
2500
0
    Preferences::GetBool("browser.link.open_newwindow.disabled_in_fullscreen");
2501
0
2502
0
  if (isDisabledOpenNewWindow &&
2503
0
      (containerPref == nsIBrowserDOMWindow::OPEN_NEWWINDOW)) {
2504
0
    containerPref = nsIBrowserDOMWindow::OPEN_NEWTAB;
2505
0
  }
2506
0
2507
0
  if (containerPref != nsIBrowserDOMWindow::OPEN_NEWTAB &&
2508
0
      containerPref != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
2509
0
    // Just open a window normally
2510
0
    return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
2511
0
  }
2512
0
2513
0
  if (aCalledFromJS) {
2514
0
    /* Now check our restriction pref.  The restriction pref is a power-user's
2515
0
       fine-tuning pref. values:
2516
0
       0: no restrictions - divert everything
2517
0
       1: don't divert window.open at all
2518
0
       2: don't divert window.open with features
2519
0
    */
2520
0
    int32_t restrictionPref =
2521
0
      Preferences::GetInt("browser.link.open_newwindow.restriction", 2);
2522
0
    if (restrictionPref < 0 || restrictionPref > 2) {
2523
0
      restrictionPref = 2; // Sane default behavior
2524
0
    }
2525
0
2526
0
    if (isDisabledOpenNewWindow) {
2527
0
      // In browser fullscreen, the window should be opened
2528
0
      // in the current window with no features (see bug 803675)
2529
0
      restrictionPref = 0;
2530
0
    }
2531
0
2532
0
    if (restrictionPref == 1) {
2533
0
      return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
2534
0
    }
2535
0
2536
0
    if (restrictionPref == 2) {
2537
0
      // Only continue if there are no size/position features and no special
2538
0
      // chrome flags - with the exception of the remoteness and private flags,
2539
0
      // which might have been automatically flipped by Gecko.
2540
0
      int32_t uiChromeFlags = aChromeFlags;
2541
0
      uiChromeFlags &= ~(nsIWebBrowserChrome::CHROME_REMOTE_WINDOW |
2542
0
                         nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
2543
0
                         nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
2544
0
                         nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
2545
0
      if (uiChromeFlags != nsIWebBrowserChrome::CHROME_ALL ||
2546
0
          aPositionSpecified || aSizeSpecified) {
2547
0
        return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
2548
0
      }
2549
0
    }
2550
0
  }
2551
0
2552
0
  return containerPref;
2553
0
}