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