Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
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/. */
7 //#define USEWEAKREFS // (haven't quite figured that out yet)
9 #include "nsWindowWatcher.h"
10 #include "nsAutoWindowStateHelper.h"
12 #include "nsCRT.h"
13 #include "nsNetUtil.h"
14 #include "nsJSUtils.h"
15 #include "plstr.h"
17 #include "nsIBaseWindow.h"
18 #include "nsIDocShell.h"
19 #include "nsIDocShellLoadInfo.h"
20 #include "nsIDocShellTreeItem.h"
21 #include "nsIDocShellTreeOwner.h"
22 #include "nsIDocumentLoader.h"
23 #include "nsIDocument.h"
24 #include "nsIDOMDocument.h"
25 #include "nsIDOMWindow.h"
26 #include "nsIDOMChromeWindow.h"
27 #include "nsIDOMModalContentWindow.h"
28 #include "nsIPrompt.h"
29 #include "nsIScriptObjectPrincipal.h"
30 #include "nsIScreen.h"
31 #include "nsIScreenManager.h"
32 #include "nsIScriptContext.h"
33 #include "nsIObserverService.h"
34 #include "nsIScriptGlobalObject.h"
35 #include "nsIScriptSecurityManager.h"
36 #include "nsXPCOM.h"
37 #include "nsIURI.h"
38 #include "nsIWebBrowser.h"
39 #include "nsIWebBrowserChrome.h"
40 #include "nsIWebNavigation.h"
41 #include "nsIWindowCreator.h"
42 #include "nsIWindowCreator2.h"
43 #include "nsIXPConnect.h"
44 #include "nsIXULRuntime.h"
45 #include "nsPIDOMWindow.h"
46 #include "nsIMarkupDocumentViewer.h"
47 #include "nsIContentViewer.h"
48 #include "nsIWindowProvider.h"
49 #include "nsIMutableArray.h"
50 #include "nsISupportsArray.h"
51 #include "nsIDOMStorage.h"
52 #include "nsIDOMStorageManager.h"
53 #include "nsIWidget.h"
54 #include "nsFocusManager.h"
55 #include "nsIPresShell.h"
56 #include "nsPresContext.h"
57 #include "nsContentUtils.h"
58 #include "nsCxPusher.h"
59 #include "nsIPrefBranch.h"
60 #include "nsIPrefService.h"
61 #include "nsSandboxFlags.h"
62 #include "mozilla/Preferences.h"
64 #ifdef USEWEAKREFS
65 #include "nsIWeakReference.h"
66 #endif
68 using namespace mozilla;
70 /****************************************************************
71 ******************** nsWatcherWindowEntry **********************
72 ****************************************************************/
74 class nsWindowWatcher;
76 struct nsWatcherWindowEntry {
78 nsWatcherWindowEntry(nsIDOMWindow *inWindow, nsIWebBrowserChrome *inChrome) {
79 #ifdef USEWEAKREFS
80 mWindow = do_GetWeakReference(inWindow);
81 #else
82 mWindow = inWindow;
83 #endif
84 nsCOMPtr<nsISupportsWeakReference> supportsweak(do_QueryInterface(inChrome));
85 if (supportsweak) {
86 supportsweak->GetWeakReference(getter_AddRefs(mChromeWeak));
87 } else {
88 mChrome = inChrome;
89 mChromeWeak = 0;
90 }
91 ReferenceSelf();
92 }
93 ~nsWatcherWindowEntry() {}
95 void InsertAfter(nsWatcherWindowEntry *inOlder);
96 void Unlink();
97 void ReferenceSelf();
99 #ifdef USEWEAKREFS
100 nsCOMPtr<nsIWeakReference> mWindow;
101 #else // still not an owning ref
102 nsIDOMWindow *mWindow;
103 #endif
104 nsIWebBrowserChrome *mChrome;
105 nsWeakPtr mChromeWeak;
106 // each struct is in a circular, doubly-linked list
107 nsWatcherWindowEntry *mYounger, // next younger in sequence
108 *mOlder;
109 };
111 void nsWatcherWindowEntry::InsertAfter(nsWatcherWindowEntry *inOlder)
112 {
113 if (inOlder) {
114 mOlder = inOlder;
115 mYounger = inOlder->mYounger;
116 mOlder->mYounger = this;
117 if (mOlder->mOlder == mOlder)
118 mOlder->mOlder = this;
119 mYounger->mOlder = this;
120 if (mYounger->mYounger == mYounger)
121 mYounger->mYounger = this;
122 }
123 }
125 void nsWatcherWindowEntry::Unlink() {
127 mOlder->mYounger = mYounger;
128 mYounger->mOlder = mOlder;
129 ReferenceSelf();
130 }
132 void nsWatcherWindowEntry::ReferenceSelf() {
134 mYounger = this;
135 mOlder = this;
136 }
138 /****************************************************************
139 ****************** nsWatcherWindowEnumerator *******************
140 ****************************************************************/
142 class nsWatcherWindowEnumerator : public nsISimpleEnumerator {
144 public:
145 nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher);
146 virtual ~nsWatcherWindowEnumerator();
147 NS_IMETHOD HasMoreElements(bool *retval);
148 NS_IMETHOD GetNext(nsISupports **retval);
150 NS_DECL_ISUPPORTS
152 private:
153 friend class nsWindowWatcher;
155 nsWatcherWindowEntry *FindNext();
156 void WindowRemoved(nsWatcherWindowEntry *inInfo);
158 nsWindowWatcher *mWindowWatcher;
159 nsWatcherWindowEntry *mCurrentPosition;
160 };
162 NS_IMPL_ADDREF(nsWatcherWindowEnumerator)
163 NS_IMPL_RELEASE(nsWatcherWindowEnumerator)
164 NS_IMPL_QUERY_INTERFACE(nsWatcherWindowEnumerator, nsISimpleEnumerator)
166 nsWatcherWindowEnumerator::nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher)
167 : mWindowWatcher(inWatcher),
168 mCurrentPosition(inWatcher->mOldestWindow)
169 {
170 mWindowWatcher->AddEnumerator(this);
171 mWindowWatcher->AddRef();
172 }
174 nsWatcherWindowEnumerator::~nsWatcherWindowEnumerator()
175 {
176 mWindowWatcher->RemoveEnumerator(this);
177 mWindowWatcher->Release();
178 }
180 NS_IMETHODIMP
181 nsWatcherWindowEnumerator::HasMoreElements(bool *retval)
182 {
183 if (!retval)
184 return NS_ERROR_INVALID_ARG;
186 *retval = mCurrentPosition? true : false;
187 return NS_OK;
188 }
190 NS_IMETHODIMP
191 nsWatcherWindowEnumerator::GetNext(nsISupports **retval)
192 {
193 if (!retval)
194 return NS_ERROR_INVALID_ARG;
196 *retval = nullptr;
198 #ifdef USEWEAKREFS
199 while (mCurrentPosition) {
200 CallQueryReferent(mCurrentPosition->mWindow, retval);
201 if (*retval) {
202 mCurrentPosition = FindNext();
203 break;
204 } else // window is gone!
205 mWindowWatcher->RemoveWindow(mCurrentPosition);
206 }
207 NS_IF_ADDREF(*retval);
208 #else
209 if (mCurrentPosition) {
210 CallQueryInterface(mCurrentPosition->mWindow, retval);
211 mCurrentPosition = FindNext();
212 }
213 #endif
214 return NS_OK;
215 }
217 nsWatcherWindowEntry *
218 nsWatcherWindowEnumerator::FindNext()
219 {
220 nsWatcherWindowEntry *info;
222 if (!mCurrentPosition)
223 return 0;
225 info = mCurrentPosition->mYounger;
226 return info == mWindowWatcher->mOldestWindow ? 0 : info;
227 }
229 // if a window is being removed adjust the iterator's current position
230 void nsWatcherWindowEnumerator::WindowRemoved(nsWatcherWindowEntry *inInfo) {
232 if (mCurrentPosition == inInfo)
233 mCurrentPosition = mCurrentPosition != inInfo->mYounger ?
234 inInfo->mYounger : 0;
235 }
237 /****************************************************************
238 *********************** nsWindowWatcher ************************
239 ****************************************************************/
241 NS_IMPL_ADDREF(nsWindowWatcher)
242 NS_IMPL_RELEASE(nsWindowWatcher)
243 NS_IMPL_QUERY_INTERFACE(nsWindowWatcher,
244 nsIWindowWatcher,
245 nsIPromptFactory,
246 nsPIWindowWatcher)
248 nsWindowWatcher::nsWindowWatcher() :
249 mEnumeratorList(),
250 mOldestWindow(0),
251 mListLock("nsWindowWatcher.mListLock")
252 {
253 }
255 nsWindowWatcher::~nsWindowWatcher()
256 {
257 // delete data
258 while (mOldestWindow)
259 RemoveWindow(mOldestWindow);
260 }
262 nsresult
263 nsWindowWatcher::Init()
264 {
265 return NS_OK;
266 }
268 /**
269 * Convert aArguments into either an nsIArray or nullptr.
270 *
271 * - If aArguments is nullptr, return nullptr.
272 * - If aArguments is an nsArray, return nullptr if it's empty, or otherwise
273 * return the array.
274 * - If aArguments is an nsISupportsArray, return nullptr if it's empty, or
275 * otherwise add its elements to an nsArray and return the new array.
276 * - Otherwise, return an nsIArray with one element: aArguments.
277 */
278 static already_AddRefed<nsIArray>
279 ConvertArgsToArray(nsISupports* aArguments)
280 {
281 if (!aArguments) {
282 return nullptr;
283 }
285 nsCOMPtr<nsIArray> array = do_QueryInterface(aArguments);
286 if (array) {
287 uint32_t argc = 0;
288 array->GetLength(&argc);
289 if (argc == 0)
290 return nullptr;
292 return array.forget();
293 }
295 nsCOMPtr<nsISupportsArray> supArray = do_QueryInterface(aArguments);
296 if (supArray) {
297 uint32_t argc = 0;
298 supArray->Count(&argc);
299 if (argc == 0) {
300 return nullptr;
301 }
303 nsCOMPtr<nsIMutableArray> mutableArray =
304 do_CreateInstance(NS_ARRAY_CONTRACTID);
305 NS_ENSURE_TRUE(mutableArray, nullptr);
307 for (uint32_t i = 0; i < argc; i++) {
308 nsCOMPtr<nsISupports> elt;
309 supArray->GetElementAt(i, getter_AddRefs(elt));
310 nsresult rv = mutableArray->AppendElement(elt, /* aWeak = */ false);
311 NS_ENSURE_SUCCESS(rv, nullptr);
312 }
314 return mutableArray.forget();
315 }
317 nsCOMPtr<nsIMutableArray> singletonArray =
318 do_CreateInstance(NS_ARRAY_CONTRACTID);
319 NS_ENSURE_TRUE(singletonArray, nullptr);
321 nsresult rv = singletonArray->AppendElement(aArguments, /* aWeak = */ false);
322 NS_ENSURE_SUCCESS(rv, nullptr);
324 return singletonArray.forget();
325 }
327 NS_IMETHODIMP
328 nsWindowWatcher::OpenWindow(nsIDOMWindow *aParent,
329 const char *aUrl,
330 const char *aName,
331 const char *aFeatures,
332 nsISupports *aArguments,
333 nsIDOMWindow **_retval)
334 {
335 nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
337 uint32_t argc = 0;
338 if (argv) {
339 argv->GetLength(&argc);
340 }
341 bool dialog = (argc != 0);
343 return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
344 /* calledFromJS = */ false, dialog,
345 /* navigate = */ true, argv, _retval);
346 }
348 struct SizeSpec {
349 SizeSpec() :
350 mLeftSpecified(false),
351 mTopSpecified(false),
352 mOuterWidthSpecified(false),
353 mOuterHeightSpecified(false),
354 mInnerWidthSpecified(false),
355 mInnerHeightSpecified(false),
356 mUseDefaultWidth(false),
357 mUseDefaultHeight(false)
358 {}
360 int32_t mLeft;
361 int32_t mTop;
362 int32_t mOuterWidth; // Total window width
363 int32_t mOuterHeight; // Total window height
364 int32_t mInnerWidth; // Content area width
365 int32_t mInnerHeight; // Content area height
367 bool mLeftSpecified;
368 bool mTopSpecified;
369 bool mOuterWidthSpecified;
370 bool mOuterHeightSpecified;
371 bool mInnerWidthSpecified;
372 bool mInnerHeightSpecified;
374 // If these booleans are true, don't look at the corresponding width values
375 // even if they're specified -- they'll be bogus
376 bool mUseDefaultWidth;
377 bool mUseDefaultHeight;
379 bool PositionSpecified() const {
380 return mLeftSpecified || mTopSpecified;
381 }
383 bool SizeSpecified() const {
384 return mOuterWidthSpecified || mOuterHeightSpecified ||
385 mInnerWidthSpecified || mInnerHeightSpecified;
386 }
387 };
389 NS_IMETHODIMP
390 nsWindowWatcher::OpenWindow2(nsIDOMWindow *aParent,
391 const char *aUrl,
392 const char *aName,
393 const char *aFeatures,
394 bool aCalledFromScript,
395 bool aDialog,
396 bool aNavigate,
397 nsISupports *aArguments,
398 nsIDOMWindow **_retval)
399 {
400 nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
402 uint32_t argc = 0;
403 if (argv) {
404 argv->GetLength(&argc);
405 }
407 // This is extremely messed up, but this behavior is necessary because
408 // callers lie about whether they're a dialog window and whether they're
409 // called from script. Fixing this is bug 779939.
410 bool dialog = aDialog;
411 if (!aCalledFromScript) {
412 dialog = argc > 0;
413 }
415 return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
416 aCalledFromScript, dialog,
417 aNavigate, argv, _retval);
418 }
420 nsresult
421 nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
422 const char *aUrl,
423 const char *aName,
424 const char *aFeatures,
425 bool aCalledFromJS,
426 bool aDialog,
427 bool aNavigate,
428 nsIArray *argv,
429 nsIDOMWindow **_retval)
430 {
431 nsresult rv = NS_OK;
432 bool nameSpecified,
433 featuresSpecified,
434 isNewToplevelWindow = false,
435 windowIsNew = false,
436 windowNeedsName = false,
437 windowIsModal = false,
438 uriToLoadIsChrome = false,
439 windowIsModalContentDialog = false;
440 uint32_t chromeFlags;
441 nsAutoString name; // string version of aName
442 nsAutoCString features; // string version of aFeatures
443 nsCOMPtr<nsIURI> uriToLoad; // from aUrl, if any
444 nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; // from the parent window, if any
445 nsCOMPtr<nsIDocShellTreeItem> newDocShellItem; // from the new window
446 nsCxPusher callerContextGuard;
448 NS_ENSURE_ARG_POINTER(_retval);
449 *_retval = 0;
451 if (!nsContentUtils::IsSafeToRunScript()) {
452 return NS_ERROR_FAILURE;
453 }
455 GetWindowTreeOwner(aParent, getter_AddRefs(parentTreeOwner));
457 if (aUrl) {
458 rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad));
459 if (NS_FAILED(rv))
460 return rv;
461 uriToLoad->SchemeIs("chrome", &uriToLoadIsChrome);
462 }
464 nameSpecified = false;
465 if (aName) {
466 CopyUTF8toUTF16(aName, name);
467 nameSpecified = true;
468 }
470 featuresSpecified = false;
471 if (aFeatures) {
472 features.Assign(aFeatures);
473 featuresSpecified = true;
474 features.StripWhitespace();
475 }
477 // try to find an extant window with the given name
478 nsCOMPtr<nsIDOMWindow> foundWindow = SafeGetWindowByName(name, aParent);
479 GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
481 // Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI.
482 // The state of the window can change before this call and if we are blocked
483 // because of sandboxing, we wouldn't want that to happen.
484 nsCOMPtr<nsPIDOMWindow> parentWindow = do_QueryInterface(aParent);
485 nsCOMPtr<nsIDocShell> parentDocShell;
486 if (parentWindow) {
487 parentDocShell = parentWindow->GetDocShell();
488 if (parentDocShell) {
489 nsCOMPtr<nsIDocShell> foundDocShell = do_QueryInterface(newDocShellItem);
490 if (parentDocShell->IsSandboxedFrom(foundDocShell)) {
491 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
492 }
493 }
494 }
496 // no extant window? make a new one.
498 // If no parent, consider it chrome.
499 bool hasChromeParent = true;
500 if (aParent) {
501 // Check if the parent document has chrome privileges.
502 nsCOMPtr<nsIDOMDocument> domdoc;
503 aParent->GetDocument(getter_AddRefs(domdoc));
504 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
505 hasChromeParent = doc && nsContentUtils::IsChromeDoc(doc);
506 }
508 // Make sure we call CalculateChromeFlags() *before* we push the
509 // callee context onto the context stack so that
510 // CalculateChromeFlags() sees the actual caller when doing its
511 // security checks.
512 chromeFlags = CalculateChromeFlags(aParent, features.get(), featuresSpecified,
513 aDialog, uriToLoadIsChrome,
514 hasChromeParent);
516 // If we're not called through our JS version of the API, and we got
517 // our internal modal option, treat the window we're opening as a
518 // modal content window (and set the modal chrome flag).
519 if (!aCalledFromJS && argv &&
520 WinHasOption(features.get(), "-moz-internal-modal", 0, nullptr)) {
521 windowIsModalContentDialog = true;
523 // CHROME_MODAL gets inherited by dependent windows, which affects various
524 // platform-specific window state (especially on OSX). So we need some way
525 // to determine that this window was actually opened by nsGlobalWindow::
526 // ShowModalDialog(), and that somebody is actually going to be watching
527 // for return values and all that.
528 chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL_CONTENT_WINDOW;
529 chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL;
530 }
532 SizeSpec sizeSpec;
533 CalcSizeSpec(features.get(), sizeSpec);
535 nsCOMPtr<nsIScriptSecurityManager>
536 sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
538 bool isCallerChrome = nsContentUtils::IsCallerChrome();
540 JSContext *cx = GetJSContextFromWindow(aParent);
542 bool windowTypeIsChrome = chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
543 if (isCallerChrome && !hasChromeParent && !windowTypeIsChrome && cx) {
544 // open() is called from chrome on a non-chrome window, push the context of the
545 // callee onto the context stack to prevent the caller's priveleges from leaking
546 // into code that runs while opening the new window.
547 //
548 // The reasoning for this is in bug 289204. Basically, chrome sometimes does
549 // someContentWindow.open(untrustedURL), and wants to be insulated from nasty
550 // javascript: URLs and such. But there are also cases where we create a
551 // window parented to a content window (such as a download dialog), usually
552 // directly with nsIWindowWatcher. In those cases, we want the principal of
553 // the initial about:blank document to be system, so that the subsequent XUL
554 // load can reuse the inner window and avoid blowing away expandos. As such,
555 // we decide whether to load with the principal of the caller or of the parent
556 // based on whether the docshell type is chrome or content.
558 callerContextGuard.Push(cx);
559 }
561 uint32_t activeDocsSandboxFlags = 0;
562 if (!newDocShellItem) {
563 // We're going to either open up a new window ourselves or ask a
564 // nsIWindowProvider for one. In either case, we'll want to set the right
565 // name on it.
566 windowNeedsName = true;
568 // If the parent trying to open a new window is sandboxed
569 // without 'allow-popups', this is not allowed and we fail here.
570 if (aParent) {
571 nsCOMPtr<nsIDOMDocument> domdoc;
572 aParent->GetDocument(getter_AddRefs(domdoc));
573 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
575 if (doc) {
576 // Save sandbox flags for copying to new browsing context (docShell).
577 activeDocsSandboxFlags = doc->GetSandboxFlags();
578 if (activeDocsSandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
579 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
580 }
581 }
582 }
584 // Now check whether it's ok to ask a window provider for a window. Don't
585 // do it if we're opening a dialog or if our parent is a chrome window or
586 // if we're opening something that has modal, dialog, or chrome flags set.
587 nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(aParent);
588 if (!aDialog && !chromeWin &&
589 !(chromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
590 nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
591 nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
592 nsCOMPtr<nsIWindowProvider> provider = do_GetInterface(parentTreeOwner);
593 if (provider) {
594 NS_ASSERTION(aParent, "We've _got_ to have a parent here!");
596 nsCOMPtr<nsIDOMWindow> newWindow;
597 rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS,
598 sizeSpec.PositionSpecified(),
599 sizeSpec.SizeSpecified(),
600 uriToLoad, name, features, &windowIsNew,
601 getter_AddRefs(newWindow));
603 if (NS_SUCCEEDED(rv)) {
604 GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
605 if (windowIsNew && newDocShellItem) {
606 // Make sure to stop any loads happening in this window that the
607 // window provider might have started. Otherwise if our caller
608 // manipulates the window it just opened and then the load
609 // completes their stuff will get blown away.
610 nsCOMPtr<nsIWebNavigation> webNav =
611 do_QueryInterface(newDocShellItem);
612 webNav->Stop(nsIWebNavigation::STOP_NETWORK);
613 }
614 }
615 else if (rv == NS_ERROR_ABORT) {
616 // NS_ERROR_ABORT means the window provider has flat-out rejected
617 // the open-window call and we should bail. Don't return an error
618 // here, because our caller may propagate that error, which might
619 // cause e.g. window.open to throw! Just return null for our out
620 // param.
621 return NS_OK;
622 }
623 }
624 }
625 }
627 bool newWindowShouldBeModal = false;
628 bool parentIsModal = false;
629 if (!newDocShellItem) {
630 windowIsNew = true;
631 isNewToplevelWindow = true;
633 nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
635 // is the parent (if any) modal? if so, we must be, too.
636 bool weAreModal = (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) != 0;
637 newWindowShouldBeModal = weAreModal;
638 if (!weAreModal && parentChrome) {
639 parentChrome->IsWindowModal(&weAreModal);
640 parentIsModal = weAreModal;
641 }
643 if (weAreModal) {
644 windowIsModal = true;
645 // in case we added this because weAreModal
646 chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL |
647 nsIWebBrowserChrome::CHROME_DEPENDENT;
648 }
650 // Make sure to not create modal windows if our parent is invisible and
651 // isn't a chrome window. Otherwise we can end up in a bizarre situation
652 // where we can't shut down because an invisible window is open. If
653 // someone tries to do this, throw.
654 if (!hasChromeParent && (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)) {
655 nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(parentTreeOwner));
656 nsCOMPtr<nsIWidget> parentWidget;
657 if (parentWindow)
658 parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
659 // NOTE: the logic for this visibility check is duplicated in
660 // nsIDOMWindowUtils::isParentWindowMainWidgetVisible - if we change
661 // how a window is determined "visible" in this context then we should
662 // also adjust that attribute and/or any consumers of it...
663 if (parentWidget && !parentWidget->IsVisible())
664 return NS_ERROR_NOT_AVAILABLE;
665 }
667 NS_ASSERTION(mWindowCreator,
668 "attempted to open a new window with no WindowCreator");
669 rv = NS_ERROR_FAILURE;
670 if (mWindowCreator) {
671 nsCOMPtr<nsIWebBrowserChrome> newChrome;
673 /* If the window creator is an nsIWindowCreator2, we can give it
674 some hints. The only hint at this time is whether the opening window
675 is in a situation that's likely to mean this is an unrequested
676 popup window we're creating. However we're not completely honest:
677 we clear that indicator if the opener is chrome, so that the
678 downstream consumer can treat the indicator to mean simply
679 that the new window is subject to popup control. */
680 nsCOMPtr<nsIWindowCreator2> windowCreator2(do_QueryInterface(mWindowCreator));
681 if (windowCreator2) {
682 uint32_t contextFlags = 0;
683 bool popupConditions = false;
685 // is the parent under popup conditions?
686 if (parentWindow) {
687 popupConditions = parentWindow->IsLoadingOrRunningTimeout();
688 }
690 // chrome is always allowed, so clear the flag if the opener is chrome
691 if (popupConditions) {
692 popupConditions = !isCallerChrome;
693 }
695 if (popupConditions)
696 contextFlags |= nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT;
698 bool cancel = false;
699 rv = windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags,
700 contextFlags, uriToLoad,
701 &cancel,
702 getter_AddRefs(newChrome));
703 if (NS_SUCCEEDED(rv) && cancel) {
704 newChrome = 0; // just in case
705 rv = NS_ERROR_ABORT;
706 }
707 }
708 else
709 rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
710 getter_AddRefs(newChrome));
711 if (newChrome) {
712 /* It might be a chrome nsXULWindow, in which case it won't have
713 an nsIDOMWindow (primary content shell). But in that case, it'll
714 be able to hand over an nsIDocShellTreeItem directly. */
715 nsCOMPtr<nsIDOMWindow> newWindow(do_GetInterface(newChrome));
716 if (newWindow)
717 GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
718 if (!newDocShellItem)
719 newDocShellItem = do_GetInterface(newChrome);
720 if (!newDocShellItem)
721 rv = NS_ERROR_FAILURE;
722 }
723 }
724 }
726 // better have a window to use by this point
727 if (!newDocShellItem)
728 return rv;
730 nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
731 NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED);
733 // Set up sandboxing attributes if the window is new.
734 // The flags can only be non-zero for new windows.
735 if (activeDocsSandboxFlags != 0) {
736 newDocShell->SetSandboxFlags(activeDocsSandboxFlags);
737 if (parentWindow) {
738 newDocShell->
739 SetOnePermittedSandboxedNavigator(parentWindow->GetDocShell());
740 }
741 }
743 rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, windowIsNew, _retval);
744 if (NS_FAILED(rv))
745 return rv;
747 /* disable persistence of size/position in popups (determined by
748 determining whether the features parameter specifies width or height
749 in any way). We consider any overriding of the window's size or position
750 in the open call as disabling persistence of those attributes.
751 Popup windows (which should not persist size or position) generally set
752 the size. */
753 if (isNewToplevelWindow) {
754 /* at the moment, the strings "height=" or "width=" never happen
755 outside a size specification, so we can do this the Q&D way. */
757 if (PL_strcasestr(features.get(), "width=") || PL_strcasestr(features.get(), "height=")) {
759 nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
760 newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
761 if (newTreeOwner)
762 newTreeOwner->SetPersistence(false, false, false);
763 }
764 }
766 if ((aDialog || windowIsModalContentDialog) && argv) {
767 // Set the args on the new window.
768 nsCOMPtr<nsPIDOMWindow> piwin(do_QueryInterface(*_retval));
769 NS_ENSURE_TRUE(piwin, NS_ERROR_UNEXPECTED);
771 rv = piwin->SetArguments(argv);
772 NS_ENSURE_SUCCESS(rv, rv);
773 }
775 /* allow a window that we found by name to keep its name (important for cases
776 like _self where the given name is different (and invalid)). Also, _blank
777 is not a window name. */
778 if (windowNeedsName) {
779 if (nameSpecified && !name.LowerCaseEqualsLiteral("_blank")) {
780 newDocShellItem->SetName(name);
781 } else {
782 newDocShellItem->SetName(EmptyString());
783 }
784 }
786 // Now we have to set the right opener principal on the new window. Note
787 // that we have to do this _before_ starting any URI loads, thanks to the
788 // sync nature of javascript: loads. Since this is the only place where we
789 // set said opener principal, we need to do it for all URIs, including
790 // chrome ones. So to deal with the mess that is bug 79775, just press on in
791 // a reasonable way even if GetSubjectPrincipal fails. In that case, just
792 // use a null subjectPrincipal.
793 nsCOMPtr<nsIPrincipal> subjectPrincipal;
794 if (NS_FAILED(sm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)))) {
795 subjectPrincipal = nullptr;
796 }
798 if (windowIsNew) {
799 // Now set the opener principal on the new window. Note that we need to do
800 // this no matter whether we were opened from JS; if there is nothing on
801 // the JS stack, just use the principal of our parent window. In those
802 // cases we do _not_ set the parent window principal as the owner of the
803 // load--since we really don't know who the owner is, just leave it null.
804 nsCOMPtr<nsPIDOMWindow> newWindow = do_QueryInterface(*_retval);
805 #ifdef DEBUG
806 nsCOMPtr<nsPIDOMWindow> newDebugWindow = do_GetInterface(newDocShell);
807 NS_ASSERTION(newWindow == newDebugWindow, "Different windows??");
808 #endif
809 // The principal of the initial about:blank document gets set up in
810 // nsWindowWatcher::AddWindow. Make sure to call it. In the common case
811 // this call already happened when the window was created, but
812 // SetInitialPrincipalToSubject is safe to call multiple times.
813 if (newWindow) {
814 newWindow->SetInitialPrincipalToSubject();
815 }
816 }
818 // If all windows should be private, make sure the new window is also
819 // private. Otherwise, see if the caller has explicitly requested a
820 // private or non-private window.
821 bool isPrivateBrowsingWindow =
822 Preferences::GetBool("browser.privatebrowsing.autostart") ||
823 (!!(chromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) &&
824 !(chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW));
826 // Otherwise, propagate the privacy status of the parent window, if
827 // available, to the child.
828 if (!isPrivateBrowsingWindow &&
829 !(chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW)) {
830 nsCOMPtr<nsIDocShellTreeItem> parentItem;
831 GetWindowTreeItem(aParent, getter_AddRefs(parentItem));
832 nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(parentItem);
833 if (parentContext) {
834 isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
835 }
836 }
838 // We rely on CalculateChromeFlags to decide whether remote (out-of-process)
839 // tabs should be used.
840 bool isRemoteWindow =
841 !!(chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
843 if (isNewToplevelWindow) {
844 nsCOMPtr<nsIDocShellTreeItem> childRoot;
845 newDocShellItem->GetRootTreeItem(getter_AddRefs(childRoot));
846 nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(childRoot);
847 if (childContext) {
848 childContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
849 childContext->SetRemoteTabs(isRemoteWindow);
850 }
851 } else if (windowIsNew) {
852 nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(newDocShellItem);
853 if (childContext) {
854 childContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
855 childContext->SetRemoteTabs(isRemoteWindow);
856 }
857 }
859 nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
860 if (uriToLoad && aNavigate) { // get the script principal and pass it to docshell
862 // The historical ordering of attempts here is:
863 // (1) Stack-top cx.
864 // (2) cx associated with aParent.
865 // (3) Safe JSContext.
866 //
867 // We could just use an AutoJSContext here if it weren't for (2), which
868 // is probably nonsensical anyway. But we preserve the old semantics for
869 // now, because this is part of a large patch where we don't want to risk
870 // subtle behavioral modifications.
871 cx = nsContentUtils::GetCurrentJSContext();
872 nsCxPusher pusher;
873 if (!cx) {
874 cx = GetJSContextFromWindow(aParent);
875 if (!cx)
876 cx = nsContentUtils::GetSafeJSContext();
877 pusher.Push(cx);
878 }
880 newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
881 NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
883 if (subjectPrincipal) {
884 loadInfo->SetOwner(subjectPrincipal);
885 }
887 // Set the new window's referrer from the calling context's document:
889 // get its document, if any
890 JSContext* ccx = nsContentUtils::GetCurrentJSContext();
891 if (ccx) {
892 nsIScriptGlobalObject *sgo = nsJSUtils::GetDynamicScriptGlobal(ccx);
894 nsCOMPtr<nsPIDOMWindow> w(do_QueryInterface(sgo));
895 if (w) {
896 /* use the URL from the *extant* document, if any. The usual accessor
897 GetDocument will synchronously create an about:blank document if
898 it has no better answer, and we only care about a real document.
899 Also using GetDocument to force document creation seems to
900 screw up focus in the hidden window; see bug 36016.
901 */
902 nsCOMPtr<nsIDocument> doc = w->GetExtantDoc();
903 if (doc) {
904 // Set the referrer
905 loadInfo->SetReferrer(doc->GetDocumentURI());
906 }
907 }
908 }
909 }
911 if (isNewToplevelWindow) {
912 // Notify observers that the window is open and ready.
913 // The window has not yet started to load a document.
914 nsCOMPtr<nsIObserverService> obsSvc =
915 mozilla::services::GetObserverService();
916 if (obsSvc)
917 obsSvc->NotifyObservers(*_retval, "toplevel-window-ready", nullptr);
918 }
920 if (uriToLoad && aNavigate) {
921 newDocShell->LoadURI(uriToLoad,
922 loadInfo,
923 windowIsNew
924 ? static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD)
925 : static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_NONE),
926 true);
927 }
929 // Copy the current session storage for the current domain.
930 if (subjectPrincipal && parentDocShell) {
931 nsCOMPtr<nsIDOMStorageManager> parentStorageManager = do_QueryInterface(parentDocShell);
932 nsCOMPtr<nsIDOMStorageManager> newStorageManager = do_QueryInterface(newDocShell);
934 if (parentStorageManager && newStorageManager) {
935 nsCOMPtr<nsIDOMStorage> storage;
936 parentStorageManager->GetStorageForFirstParty(uriToLoad, subjectPrincipal,
937 isPrivateBrowsingWindow, getter_AddRefs(storage));
938 if (storage)
939 newStorageManager->CloneStorage(storage);
940 }
941 }
943 if (isNewToplevelWindow)
944 SizeOpenedDocShellItem(newDocShellItem, aParent, sizeSpec);
946 // XXXbz isn't windowIsModal always true when windowIsModalContentDialog?
947 if (windowIsModal || windowIsModalContentDialog) {
948 nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
949 newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
950 nsCOMPtr<nsIWebBrowserChrome> newChrome(do_GetInterface(newTreeOwner));
952 // Throw an exception here if no web browser chrome is available,
953 // we need that to show a modal window.
954 NS_ENSURE_TRUE(newChrome, NS_ERROR_NOT_AVAILABLE);
956 // Dispatch dialog events etc, but we only want to do that if
957 // we're opening a modal content window (the helper classes are
958 // no-ops if given no window), for chrome dialogs we don't want to
959 // do any of that (it's done elsewhere for us).
960 // Make sure we maintain the state on an outer window, because
961 // that's where it lives; inner windows assert if you try to
962 // maintain the state on them.
963 nsAutoWindowStateHelper windowStateHelper(
964 parentWindow ? parentWindow->GetOuterWindow() : nullptr);
966 if (!windowStateHelper.DefaultEnabled()) {
967 // Default to cancel not opening the modal window.
968 NS_RELEASE(*_retval);
970 return NS_OK;
971 }
974 if (!newWindowShouldBeModal && parentIsModal) {
975 nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(newTreeOwner));
976 if (parentWindow) {
977 nsCOMPtr<nsIWidget> parentWidget;
978 parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
979 if (parentWidget) {
980 parentWidget->SetModal(true);
981 }
982 }
983 } else {
984 // Reset popup state while opening a modal dialog, and firing
985 // events about the dialog, to prevent the current state from
986 // being active the whole time a modal dialog is open.
987 nsAutoPopupStatePusher popupStatePusher(openAbused);
989 newChrome->ShowAsModal();
990 }
991 }
993 return NS_OK;
994 }
996 NS_IMETHODIMP
997 nsWindowWatcher::RegisterNotification(nsIObserver *aObserver)
998 {
999 // just a convenience method; it delegates to nsIObserverService
1001 if (!aObserver)
1002 return NS_ERROR_INVALID_ARG;
1004 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1005 if (!os)
1006 return NS_ERROR_FAILURE;
1008 nsresult rv = os->AddObserver(aObserver, "domwindowopened", false);
1009 if (NS_SUCCEEDED(rv))
1010 rv = os->AddObserver(aObserver, "domwindowclosed", false);
1012 return rv;
1013 }
1015 NS_IMETHODIMP
1016 nsWindowWatcher::UnregisterNotification(nsIObserver *aObserver)
1017 {
1018 // just a convenience method; it delegates to nsIObserverService
1020 if (!aObserver)
1021 return NS_ERROR_INVALID_ARG;
1023 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1024 if (!os)
1025 return NS_ERROR_FAILURE;
1027 os->RemoveObserver(aObserver, "domwindowopened");
1028 os->RemoveObserver(aObserver, "domwindowclosed");
1030 return NS_OK;
1031 }
1033 NS_IMETHODIMP
1034 nsWindowWatcher::GetWindowEnumerator(nsISimpleEnumerator** _retval)
1035 {
1036 if (!_retval)
1037 return NS_ERROR_INVALID_ARG;
1039 MutexAutoLock lock(mListLock);
1040 nsWatcherWindowEnumerator *enumerator = new nsWatcherWindowEnumerator(this);
1041 if (enumerator)
1042 return CallQueryInterface(enumerator, _retval);
1044 return NS_ERROR_OUT_OF_MEMORY;
1045 }
1047 NS_IMETHODIMP
1048 nsWindowWatcher::GetNewPrompter(nsIDOMWindow *aParent, nsIPrompt **_retval)
1049 {
1050 // This is for backwards compat only. Callers should just use the prompt service directly.
1051 nsresult rv;
1052 nsCOMPtr<nsIPromptFactory> factory = do_GetService("@mozilla.org/prompter;1", &rv);
1053 NS_ENSURE_SUCCESS(rv, rv);
1054 return factory->GetPrompt(aParent, NS_GET_IID(nsIPrompt), reinterpret_cast<void**>(_retval));
1055 }
1057 NS_IMETHODIMP
1058 nsWindowWatcher::GetNewAuthPrompter(nsIDOMWindow *aParent, nsIAuthPrompt **_retval)
1059 {
1060 // This is for backwards compat only. Callers should just use the prompt service directly.
1061 nsresult rv;
1062 nsCOMPtr<nsIPromptFactory> factory = do_GetService("@mozilla.org/prompter;1", &rv);
1063 NS_ENSURE_SUCCESS(rv, rv);
1064 return factory->GetPrompt(aParent, NS_GET_IID(nsIAuthPrompt), reinterpret_cast<void**>(_retval));
1065 }
1067 NS_IMETHODIMP
1068 nsWindowWatcher::GetPrompt(nsIDOMWindow *aParent, const nsIID& aIID,
1069 void **_retval)
1070 {
1071 // This is for backwards compat only. Callers should just use the prompt service directly.
1072 nsresult rv;
1073 nsCOMPtr<nsIPromptFactory> factory = do_GetService("@mozilla.org/prompter;1", &rv);
1074 NS_ENSURE_SUCCESS(rv, rv);
1075 rv = factory->GetPrompt(aParent, aIID, _retval);
1077 // Allow for an embedding implementation to not support nsIAuthPrompt2.
1078 if (rv == NS_NOINTERFACE && aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
1079 nsCOMPtr<nsIAuthPrompt> oldPrompt;
1080 rv = factory->GetPrompt(aParent,
1081 NS_GET_IID(nsIAuthPrompt),
1082 getter_AddRefs(oldPrompt));
1083 NS_ENSURE_SUCCESS(rv, rv);
1085 NS_WrapAuthPrompt(oldPrompt, reinterpret_cast<nsIAuthPrompt2**>(_retval));
1086 if (!*_retval)
1087 rv = NS_ERROR_NOT_AVAILABLE;
1088 }
1089 return rv;
1090 }
1092 NS_IMETHODIMP
1093 nsWindowWatcher::SetWindowCreator(nsIWindowCreator *creator)
1094 {
1095 mWindowCreator = creator; // it's an nsCOMPtr, so this is an ownership ref
1096 return NS_OK;
1097 }
1099 NS_IMETHODIMP
1100 nsWindowWatcher::HasWindowCreator(bool *result)
1101 {
1102 *result = mWindowCreator;
1103 return NS_OK;
1104 }
1106 NS_IMETHODIMP
1107 nsWindowWatcher::GetActiveWindow(nsIDOMWindow **aActiveWindow)
1108 {
1109 *aActiveWindow = nullptr;
1110 nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
1111 if (fm)
1112 return fm->GetActiveWindow(aActiveWindow);
1113 return NS_OK;
1114 }
1116 NS_IMETHODIMP
1117 nsWindowWatcher::SetActiveWindow(nsIDOMWindow *aActiveWindow)
1118 {
1119 nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
1120 if (fm)
1121 return fm->SetActiveWindow(aActiveWindow);
1122 return NS_OK;
1123 }
1125 NS_IMETHODIMP
1126 nsWindowWatcher::AddWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome *aChrome)
1127 {
1128 if (!aWindow)
1129 return NS_ERROR_INVALID_ARG;
1131 #ifdef DEBUG
1132 {
1133 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aWindow));
1135 NS_ASSERTION(win->IsOuterWindow(),
1136 "Uh, the active window must be an outer window!");
1137 }
1138 #endif
1140 {
1141 nsWatcherWindowEntry *info;
1142 MutexAutoLock lock(mListLock);
1144 // if we already have an entry for this window, adjust
1145 // its chrome mapping and return
1146 info = FindWindowEntry(aWindow);
1147 if (info) {
1148 nsCOMPtr<nsISupportsWeakReference> supportsweak(do_QueryInterface(aChrome));
1149 if (supportsweak) {
1150 supportsweak->GetWeakReference(getter_AddRefs(info->mChromeWeak));
1151 } else {
1152 info->mChrome = aChrome;
1153 info->mChromeWeak = 0;
1154 }
1155 return NS_OK;
1156 }
1158 // create a window info struct and add it to the list of windows
1159 info = new nsWatcherWindowEntry(aWindow, aChrome);
1160 if (!info)
1161 return NS_ERROR_OUT_OF_MEMORY;
1163 if (mOldestWindow)
1164 info->InsertAfter(mOldestWindow->mOlder);
1165 else
1166 mOldestWindow = info;
1167 } // leave the mListLock
1169 // a window being added to us signifies a newly opened window.
1170 // send notifications.
1171 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1172 if (!os)
1173 return NS_ERROR_FAILURE;
1175 nsCOMPtr<nsISupports> domwin(do_QueryInterface(aWindow));
1176 return os->NotifyObservers(domwin, "domwindowopened", 0);
1177 }
1179 NS_IMETHODIMP
1180 nsWindowWatcher::RemoveWindow(nsIDOMWindow *aWindow)
1181 {
1182 // find the corresponding nsWatcherWindowEntry, remove it
1184 if (!aWindow)
1185 return NS_ERROR_INVALID_ARG;
1187 nsWatcherWindowEntry *info = FindWindowEntry(aWindow);
1188 if (info) {
1189 RemoveWindow(info);
1190 return NS_OK;
1191 }
1192 NS_WARNING("requested removal of nonexistent window");
1193 return NS_ERROR_INVALID_ARG;
1194 }
1196 nsWatcherWindowEntry *
1197 nsWindowWatcher::FindWindowEntry(nsIDOMWindow *aWindow)
1198 {
1199 // find the corresponding nsWatcherWindowEntry
1200 nsWatcherWindowEntry *info,
1201 *listEnd;
1202 #ifdef USEWEAKREFS
1203 nsresult rv;
1204 bool found;
1205 #endif
1207 info = mOldestWindow;
1208 listEnd = 0;
1209 #ifdef USEWEAKREFS
1210 rv = NS_OK;
1211 found = false;
1212 while (info != listEnd && NS_SUCCEEDED(rv)) {
1213 nsCOMPtr<nsIDOMWindow> infoWindow(do_QueryReferent(info->mWindow));
1214 if (!infoWindow) { // clean up dangling reference, while we're here
1215 rv = RemoveWindow(info);
1216 }
1217 else if (infoWindow.get() == aWindow)
1218 return info;
1220 info = info->mYounger;
1221 listEnd = mOldestWindow;
1222 }
1223 return 0;
1224 #else
1225 while (info != listEnd) {
1226 if (info->mWindow == aWindow)
1227 return info;
1228 info = info->mYounger;
1229 listEnd = mOldestWindow;
1230 }
1231 return 0;
1232 #endif
1233 }
1235 nsresult nsWindowWatcher::RemoveWindow(nsWatcherWindowEntry *inInfo)
1236 {
1237 uint32_t ctr,
1238 count = mEnumeratorList.Length();
1240 {
1241 // notify the enumerators
1242 MutexAutoLock lock(mListLock);
1243 for (ctr = 0; ctr < count; ++ctr)
1244 mEnumeratorList[ctr]->WindowRemoved(inInfo);
1246 // remove the element from the list
1247 if (inInfo == mOldestWindow)
1248 mOldestWindow = inInfo->mYounger == mOldestWindow ? 0 : inInfo->mYounger;
1249 inInfo->Unlink();
1250 }
1252 // a window being removed from us signifies a newly closed window.
1253 // send notifications.
1254 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1255 if (os) {
1256 #ifdef USEWEAKREFS
1257 nsCOMPtr<nsISupports> domwin(do_QueryReferent(inInfo->mWindow));
1258 if (domwin)
1259 os->NotifyObservers(domwin, "domwindowclosed", 0);
1260 // else bummer. since the window is gone, there's nothing to notify with.
1261 #else
1262 nsCOMPtr<nsISupports> domwin(do_QueryInterface(inInfo->mWindow));
1263 os->NotifyObservers(domwin, "domwindowclosed", 0);
1264 #endif
1265 }
1267 delete inInfo;
1268 return NS_OK;
1269 }
1271 NS_IMETHODIMP
1272 nsWindowWatcher::GetChromeForWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome **_retval)
1273 {
1274 if (!aWindow || !_retval)
1275 return NS_ERROR_INVALID_ARG;
1276 *_retval = 0;
1278 MutexAutoLock lock(mListLock);
1279 nsWatcherWindowEntry *info = FindWindowEntry(aWindow);
1280 if (info) {
1281 if (info->mChromeWeak != nullptr) {
1282 return info->mChromeWeak->
1283 QueryReferent(NS_GET_IID(nsIWebBrowserChrome),
1284 reinterpret_cast<void**>(_retval));
1285 }
1286 *_retval = info->mChrome;
1287 NS_IF_ADDREF(*_retval);
1288 }
1289 return NS_OK;
1290 }
1292 NS_IMETHODIMP
1293 nsWindowWatcher::GetWindowByName(const char16_t *aTargetName,
1294 nsIDOMWindow *aCurrentWindow,
1295 nsIDOMWindow **aResult)
1296 {
1297 if (!aResult) {
1298 return NS_ERROR_INVALID_ARG;
1299 }
1301 *aResult = nullptr;
1303 nsCOMPtr<nsIDocShellTreeItem> treeItem;
1305 nsCOMPtr<nsIDocShellTreeItem> startItem;
1306 GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
1307 if (startItem) {
1308 // Note: original requestor is null here, per idl comments
1309 startItem->FindItemWithName(aTargetName, nullptr, nullptr,
1310 getter_AddRefs(treeItem));
1311 }
1312 else {
1313 // Note: original requestor is null here, per idl comments
1314 FindItemWithName(aTargetName, nullptr, nullptr, getter_AddRefs(treeItem));
1315 }
1317 nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(treeItem);
1318 domWindow.swap(*aResult);
1320 return NS_OK;
1321 }
1323 bool
1324 nsWindowWatcher::AddEnumerator(nsWatcherWindowEnumerator* inEnumerator)
1325 {
1326 // (requires a lock; assumes it's called by someone holding the lock)
1327 return mEnumeratorList.AppendElement(inEnumerator) != nullptr;
1328 }
1330 bool
1331 nsWindowWatcher::RemoveEnumerator(nsWatcherWindowEnumerator* inEnumerator)
1332 {
1333 // (requires a lock; assumes it's called by someone holding the lock)
1334 return mEnumeratorList.RemoveElement(inEnumerator);
1335 }
1337 nsresult
1338 nsWindowWatcher::URIfromURL(const char *aURL,
1339 nsIDOMWindow *aParent,
1340 nsIURI **aURI)
1341 {
1342 nsCOMPtr<nsIDOMWindow> baseWindow;
1344 /* build the URI relative to the calling JS Context, if any.
1345 (note this is the same context used to make the security check
1346 in nsGlobalWindow.cpp.) */
1347 JSContext *cx = nsContentUtils::GetCurrentJSContext();
1348 if (cx) {
1349 nsIScriptContext *scriptcx = nsJSUtils::GetDynamicScriptContext(cx);
1350 if (scriptcx) {
1351 baseWindow = do_QueryInterface(scriptcx->GetGlobalObject());
1352 }
1353 }
1355 // failing that, build it relative to the parent window, if possible
1356 if (!baseWindow)
1357 baseWindow = aParent;
1359 // failing that, use the given URL unmodified. It had better not be relative.
1361 nsIURI *baseURI = nullptr;
1363 // get baseWindow's document URI
1364 if (baseWindow) {
1365 nsCOMPtr<nsIDOMDocument> domDoc;
1366 baseWindow->GetDocument(getter_AddRefs(domDoc));
1367 if (domDoc) {
1368 nsCOMPtr<nsIDocument> doc;
1369 doc = do_QueryInterface(domDoc);
1370 if (doc) {
1371 baseURI = doc->GetDocBaseURI();
1372 }
1373 }
1374 }
1376 // build and return the absolute URI
1377 return NS_NewURI(aURI, aURL, baseURI);
1378 }
1380 #define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag) \
1381 prefBranch->GetBoolPref(feature, &forceEnable); \
1382 if (forceEnable && !(aDialog && isCallerChrome) && \
1383 !(isCallerChrome && aHasChromeParent) && !aChromeURL) { \
1384 chromeFlags |= flag; \
1385 } else { \
1386 chromeFlags |= WinHasOption(aFeatures, feature, \
1387 0, &presenceFlag) \
1388 ? flag : 0; \
1389 }
1391 /**
1392 * Calculate the chrome bitmask from a string list of features.
1393 * @param aParent the opener window
1394 * @param aFeatures a string containing a list of named chrome features
1395 * @param aNullFeatures true if aFeatures was a null pointer (which fact
1396 * is lost by its conversion to a string in the caller)
1397 * @param aDialog affects the assumptions made about unnamed features
1398 * @return the chrome bitmask
1399 */
1400 // static
1401 uint32_t nsWindowWatcher::CalculateChromeFlags(nsIDOMWindow *aParent,
1402 const char *aFeatures,
1403 bool aFeaturesSpecified,
1404 bool aDialog,
1405 bool aChromeURL,
1406 bool aHasChromeParent)
1407 {
1408 if(!aFeaturesSpecified || !aFeatures) {
1409 if(aDialog)
1410 return nsIWebBrowserChrome::CHROME_ALL |
1411 nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
1412 nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
1413 else
1414 return nsIWebBrowserChrome::CHROME_ALL;
1415 }
1417 /* This function has become complicated since browser windows and
1418 dialogs diverged. The difference is, browser windows assume all
1419 chrome not explicitly mentioned is off, if the features string
1420 is not null. Exceptions are some OS border chrome new with Mozilla.
1421 Dialogs interpret a (mostly) empty features string to mean
1422 "OS's choice," and also support an "all" flag explicitly disallowed
1423 in the standards-compliant window.(normal)open. */
1425 uint32_t chromeFlags = 0;
1426 bool presenceFlag = false;
1428 chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
1429 if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag))
1430 chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
1432 /* Next, allow explicitly named options to override the initial settings */
1434 bool isCallerChrome = nsContentUtils::IsCallerChrome();
1436 // Determine whether the window is a private browsing window
1437 if (isCallerChrome) {
1438 chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ?
1439 nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0;
1440 chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag) ?
1441 nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW : 0;
1442 }
1444 // Determine whether the window should have remote tabs.
1445 if (isCallerChrome) {
1446 bool remote;
1447 if (Preferences::GetBool("browser.tabs.remote.autostart")) {
1448 remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag);
1449 } else {
1450 remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag);
1451 }
1452 if (remote) {
1453 chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
1454 }
1455 }
1457 nsresult rv;
1459 nsCOMPtr<nsIPrefBranch> prefBranch;
1460 nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
1461 NS_ENSURE_SUCCESS(rv, true);
1463 rv = prefs->GetBranch("dom.disable_window_open_feature.", getter_AddRefs(prefBranch));
1464 NS_ENSURE_SUCCESS(rv, true);
1466 bool forceEnable = false;
1468 NS_CALCULATE_CHROME_FLAG_FOR("titlebar",
1469 nsIWebBrowserChrome::CHROME_TITLEBAR);
1470 NS_CALCULATE_CHROME_FLAG_FOR("close",
1471 nsIWebBrowserChrome::CHROME_WINDOW_CLOSE);
1472 NS_CALCULATE_CHROME_FLAG_FOR("toolbar",
1473 nsIWebBrowserChrome::CHROME_TOOLBAR);
1474 NS_CALCULATE_CHROME_FLAG_FOR("location",
1475 nsIWebBrowserChrome::CHROME_LOCATIONBAR);
1476 NS_CALCULATE_CHROME_FLAG_FOR("personalbar",
1477 nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
1478 NS_CALCULATE_CHROME_FLAG_FOR("status",
1479 nsIWebBrowserChrome::CHROME_STATUSBAR);
1480 NS_CALCULATE_CHROME_FLAG_FOR("menubar",
1481 nsIWebBrowserChrome::CHROME_MENUBAR);
1482 NS_CALCULATE_CHROME_FLAG_FOR("scrollbars",
1483 nsIWebBrowserChrome::CHROME_SCROLLBARS);
1484 NS_CALCULATE_CHROME_FLAG_FOR("resizable",
1485 nsIWebBrowserChrome::CHROME_WINDOW_RESIZE);
1486 NS_CALCULATE_CHROME_FLAG_FOR("minimizable",
1487 nsIWebBrowserChrome::CHROME_WINDOW_MIN);
1489 chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag)
1490 ? nsIWebBrowserChrome::CHROME_WINDOW_POPUP : 0;
1492 /* OK.
1493 Normal browser windows, in spite of a stated pattern of turning off
1494 all chrome not mentioned explicitly, will want the new OS chrome (window
1495 borders, titlebars, closebox) on, unless explicitly turned off.
1496 Dialogs, on the other hand, take the absence of any explicit settings
1497 to mean "OS' choice." */
1499 // default titlebar and closebox to "on," if not mentioned at all
1500 if (!(chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)) {
1501 if (!PL_strcasestr(aFeatures, "titlebar"))
1502 chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
1503 if (!PL_strcasestr(aFeatures, "close"))
1504 chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
1505 }
1507 if (aDialog && !presenceFlag)
1508 chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT;
1510 /* Finally, once all the above normal chrome has been divined, deal
1511 with the features that are more operating hints than appearance
1512 instructions. (Note modality implies dependence.) */
1514 if (WinHasOption(aFeatures, "alwaysLowered", 0, nullptr) ||
1515 WinHasOption(aFeatures, "z-lock", 0, nullptr))
1516 chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
1517 else if (WinHasOption(aFeatures, "alwaysRaised", 0, nullptr))
1518 chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
1520 chromeFlags |= WinHasOption(aFeatures, "macsuppressanimation", 0, nullptr) ?
1521 nsIWebBrowserChrome::CHROME_MAC_SUPPRESS_ANIMATION : 0;
1523 chromeFlags |= WinHasOption(aFeatures, "chrome", 0, nullptr) ?
1524 nsIWebBrowserChrome::CHROME_OPENAS_CHROME : 0;
1525 chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nullptr) ?
1526 nsIWebBrowserChrome::CHROME_EXTRA : 0;
1527 chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nullptr) ?
1528 nsIWebBrowserChrome::CHROME_CENTER_SCREEN : 0;
1529 chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nullptr) ?
1530 nsIWebBrowserChrome::CHROME_DEPENDENT : 0;
1531 chromeFlags |= WinHasOption(aFeatures, "modal", 0, nullptr) ?
1532 (nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT) : 0;
1534 /* On mobile we want to ignore the dialog window feature, since the mobile UI
1535 does not provide any affordance for dialog windows. This does not interfere
1536 with dialog windows created through openDialog. */
1537 bool disableDialogFeature = false;
1538 nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
1539 branch->GetBoolPref("dom.disable_window_open_dialog_feature", &disableDialogFeature);
1541 bool isFullScreen = false;
1542 if (aParent) {
1543 aParent->GetFullScreen(&isFullScreen);
1544 }
1545 if (isFullScreen && !isCallerChrome) {
1546 // If the parent window is in fullscreen & the caller context is content,
1547 // dialog feature is disabled. (see bug 803675)
1548 disableDialogFeature = true;
1549 }
1551 if (!disableDialogFeature) {
1552 chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr) ?
1553 nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0;
1554 }
1556 /* and dialogs need to have the last word. assume dialogs are dialogs,
1557 and opened as chrome, unless explicitly told otherwise. */
1558 if (aDialog) {
1559 if (!PL_strcasestr(aFeatures, "dialog"))
1560 chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
1561 if (!PL_strcasestr(aFeatures, "chrome"))
1562 chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
1563 }
1565 /* missing
1566 chromeFlags->copy_history
1567 */
1569 // Check security state for use in determing window dimensions
1570 if (!isCallerChrome || !aHasChromeParent) {
1571 // If priv check fails (or if we're called from chrome, but the
1572 // parent is not a chrome window), set all elements to minimum
1573 // reqs., else leave them alone.
1574 chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
1575 chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
1576 chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
1577 chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
1578 chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP;
1579 /* Untrusted script is allowed to pose modal windows with a chrome
1580 scheme. This check could stand to be better. But it effectively
1581 prevents untrusted script from opening modal windows in general
1582 while still allowing alerts and the like. */
1583 if (!aChromeURL)
1584 chromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL |
1585 nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
1586 }
1588 if (!(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)) {
1589 // Remove the dependent flag if we're not opening as chrome
1590 chromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT;
1591 }
1593 // Disable CHROME_OPENAS_DIALOG if the window is inside <iframe mozbrowser>.
1594 // It's up to the embedder to interpret what dialog=1 means.
1595 nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
1596 if (docshell && docshell->GetIsInBrowserOrApp()) {
1597 chromeFlags &= ~nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
1598 }
1600 return chromeFlags;
1601 }
1603 // static
1604 int32_t
1605 nsWindowWatcher::WinHasOption(const char *aOptions, const char *aName,
1606 int32_t aDefault, bool *aPresenceFlag)
1607 {
1608 if (!aOptions)
1609 return 0;
1611 char *comma, *equal;
1612 int32_t found = 0;
1614 #ifdef DEBUG
1615 nsAutoCString options(aOptions);
1616 NS_ASSERTION(options.FindCharInSet(" \n\r\t") == kNotFound,
1617 "There should be no whitespace in this string!");
1618 #endif
1620 while (true) {
1621 comma = PL_strchr(aOptions, ',');
1622 if (comma)
1623 *comma = '\0';
1624 equal = PL_strchr(aOptions, '=');
1625 if (equal)
1626 *equal = '\0';
1627 if (nsCRT::strcasecmp(aOptions, aName) == 0) {
1628 if (aPresenceFlag)
1629 *aPresenceFlag = true;
1630 if (equal)
1631 if (*(equal + 1) == '*')
1632 found = aDefault;
1633 else if (nsCRT::strcasecmp(equal + 1, "yes") == 0)
1634 found = 1;
1635 else
1636 found = atoi(equal + 1);
1637 else
1638 found = 1;
1639 }
1640 if (equal)
1641 *equal = '=';
1642 if (comma)
1643 *comma = ',';
1644 if (found || !comma)
1645 break;
1646 aOptions = comma + 1;
1647 }
1648 return found;
1649 }
1651 /* try to find an nsIDocShellTreeItem with the given name in any
1652 known open window. a failure to find the item will not
1653 necessarily return a failure method value. check aFoundItem.
1654 */
1655 NS_IMETHODIMP
1656 nsWindowWatcher::FindItemWithName(const char16_t* aName,
1657 nsIDocShellTreeItem* aRequestor,
1658 nsIDocShellTreeItem* aOriginalRequestor,
1659 nsIDocShellTreeItem** aFoundItem)
1660 {
1661 *aFoundItem = 0;
1663 /* special cases */
1664 if(!aName || !*aName)
1665 return NS_OK;
1667 nsDependentString name(aName);
1669 nsCOMPtr<nsISimpleEnumerator> windows;
1670 GetWindowEnumerator(getter_AddRefs(windows));
1671 if (!windows)
1672 return NS_ERROR_FAILURE;
1674 bool more;
1675 nsresult rv = NS_OK;
1677 do {
1678 windows->HasMoreElements(&more);
1679 if (!more)
1680 break;
1681 nsCOMPtr<nsISupports> nextSupWindow;
1682 windows->GetNext(getter_AddRefs(nextSupWindow));
1683 nsCOMPtr<nsIDOMWindow> nextWindow(do_QueryInterface(nextSupWindow));
1684 if (nextWindow) {
1685 nsCOMPtr<nsIDocShellTreeItem> treeItem;
1686 GetWindowTreeItem(nextWindow, getter_AddRefs(treeItem));
1687 if (treeItem) {
1688 // Get the root tree item of same type, since roots are the only
1689 // things that call into the treeowner to look for named items.
1690 nsCOMPtr<nsIDocShellTreeItem> root;
1691 treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
1692 NS_ASSERTION(root, "Must have root tree item of same type");
1693 // Make sure not to call back into aRequestor
1694 if (root != aRequestor) {
1695 // Get the tree owner so we can pass it in as the requestor so
1696 // the child knows not to call back up, since we're walking
1697 // all windows already.
1698 nsCOMPtr<nsIDocShellTreeOwner> rootOwner;
1699 // Note: if we have no aRequestor, then we want to also look for
1700 // "special" window names, so pass a null requestor. This will mean
1701 // that the treeitem calls back up to us, effectively (with a
1702 // non-null aRequestor), so break the loop immediately after the
1703 // call in that case.
1704 if (aRequestor) {
1705 root->GetTreeOwner(getter_AddRefs(rootOwner));
1706 }
1707 rv = root->FindItemWithName(aName, rootOwner, aOriginalRequestor,
1708 aFoundItem);
1709 if (NS_FAILED(rv) || *aFoundItem || !aRequestor)
1710 break;
1711 }
1712 }
1713 }
1714 } while(1);
1716 return rv;
1717 }
1719 already_AddRefed<nsIDocShellTreeItem>
1720 nsWindowWatcher::GetCallerTreeItem(nsIDocShellTreeItem* aParentItem)
1721 {
1722 JSContext *cx = nsContentUtils::GetCurrentJSContext();
1723 nsCOMPtr<nsIDocShellTreeItem> callerItem;
1725 if (cx) {
1726 nsCOMPtr<nsIWebNavigation> callerWebNav =
1727 do_GetInterface(nsJSUtils::GetDynamicScriptGlobal(cx));
1729 callerItem = do_QueryInterface(callerWebNav);
1730 }
1732 if (!callerItem) {
1733 callerItem = aParentItem;
1734 }
1736 return callerItem.forget();
1737 }
1739 already_AddRefed<nsIDOMWindow>
1740 nsWindowWatcher::SafeGetWindowByName(const nsAString& aName,
1741 nsIDOMWindow* aCurrentWindow)
1742 {
1743 nsCOMPtr<nsIDocShellTreeItem> startItem;
1744 GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
1746 nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(startItem);
1748 const nsAFlatString& flatName = PromiseFlatString(aName);
1750 nsCOMPtr<nsIDocShellTreeItem> foundItem;
1751 if (startItem) {
1752 startItem->FindItemWithName(flatName.get(), nullptr, callerItem,
1753 getter_AddRefs(foundItem));
1754 }
1755 else {
1756 FindItemWithName(flatName.get(), nullptr, callerItem,
1757 getter_AddRefs(foundItem));
1758 }
1760 nsCOMPtr<nsIDOMWindow> foundWin = do_GetInterface(foundItem);
1761 return foundWin.forget();
1762 }
1764 /* Fetch the nsIDOMWindow corresponding to the given nsIDocShellTreeItem.
1765 This forces the creation of a script context, if one has not already
1766 been created. Note it also sets the window's opener to the parent,
1767 if applicable -- because it's just convenient, that's all. null aParent
1768 is acceptable. */
1769 nsresult
1770 nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem *aOpenedItem,
1771 nsIDOMWindow *aParent,
1772 bool aWindowIsNew,
1773 nsIDOMWindow **aOpenedWindow)
1774 {
1775 nsresult rv = NS_ERROR_FAILURE;
1777 *aOpenedWindow = 0;
1778 nsCOMPtr<nsPIDOMWindow> piOpenedWindow(do_GetInterface(aOpenedItem));
1779 if (piOpenedWindow) {
1780 if (aParent) {
1781 piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit
1783 if (aWindowIsNew) {
1784 #ifdef DEBUG
1785 // Assert that we're not loading things right now. If we are, when
1786 // that load completes it will clobber whatever principals we set up
1787 // on this new window!
1788 nsCOMPtr<nsIDocumentLoader> docloader =
1789 do_QueryInterface(aOpenedItem);
1790 NS_ASSERTION(docloader, "How can we not have a docloader here?");
1792 nsCOMPtr<nsIChannel> chan;
1793 docloader->GetDocumentChannel(getter_AddRefs(chan));
1794 NS_ASSERTION(!chan, "Why is there a document channel?");
1795 #endif
1797 nsCOMPtr<nsIDocument> doc = piOpenedWindow->GetExtantDoc();
1798 if (doc) {
1799 doc->SetIsInitialDocument(true);
1800 }
1801 }
1802 }
1803 rv = CallQueryInterface(piOpenedWindow, aOpenedWindow);
1804 }
1805 return rv;
1806 }
1808 // static
1809 void
1810 nsWindowWatcher::CalcSizeSpec(const char* aFeatures, SizeSpec& aResult)
1811 {
1812 // Parse position spec, if any, from aFeatures
1813 bool present;
1814 int32_t temp;
1816 present = false;
1817 if ((temp = WinHasOption(aFeatures, "left", 0, &present)) || present)
1818 aResult.mLeft = temp;
1819 else if ((temp = WinHasOption(aFeatures, "screenX", 0, &present)) || present)
1820 aResult.mLeft = temp;
1821 aResult.mLeftSpecified = present;
1823 present = false;
1824 if ((temp = WinHasOption(aFeatures, "top", 0, &present)) || present)
1825 aResult.mTop = temp;
1826 else if ((temp = WinHasOption(aFeatures, "screenY", 0, &present)) || present)
1827 aResult.mTop = temp;
1828 aResult.mTopSpecified = present;
1830 // Parse size spec, if any. Chrome size overrides content size.
1831 if ((temp = WinHasOption(aFeatures, "outerWidth", INT32_MIN, nullptr))) {
1832 if (temp == INT32_MIN) {
1833 aResult.mUseDefaultWidth = true;
1834 }
1835 else {
1836 aResult.mOuterWidth = temp;
1837 }
1838 aResult.mOuterWidthSpecified = true;
1839 } else if ((temp = WinHasOption(aFeatures, "width", INT32_MIN, nullptr)) ||
1840 (temp = WinHasOption(aFeatures, "innerWidth", INT32_MIN,
1841 nullptr))) {
1842 if (temp == INT32_MIN) {
1843 aResult.mUseDefaultWidth = true;
1844 } else {
1845 aResult.mInnerWidth = temp;
1846 }
1847 aResult.mInnerWidthSpecified = true;
1848 }
1850 if ((temp = WinHasOption(aFeatures, "outerHeight", INT32_MIN, nullptr))) {
1851 if (temp == INT32_MIN) {
1852 aResult.mUseDefaultHeight = true;
1853 }
1854 else {
1855 aResult.mOuterHeight = temp;
1856 }
1857 aResult.mOuterHeightSpecified = true;
1858 } else if ((temp = WinHasOption(aFeatures, "height", INT32_MIN,
1859 nullptr)) ||
1860 (temp = WinHasOption(aFeatures, "innerHeight", INT32_MIN,
1861 nullptr))) {
1862 if (temp == INT32_MIN) {
1863 aResult.mUseDefaultHeight = true;
1864 } else {
1865 aResult.mInnerHeight = temp;
1866 }
1867 aResult.mInnerHeightSpecified = true;
1868 }
1869 }
1871 /* Size and position the new window according to aSizeSpec. This method
1872 is assumed to be called after the window has already been given
1873 a default position and size; thus its current position and size are
1874 accurate defaults. The new window is made visible at method end.
1875 */
1876 void
1877 nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
1878 nsIDOMWindow *aParent,
1879 const SizeSpec & aSizeSpec)
1880 {
1881 // position and size of window
1882 int32_t left = 0,
1883 top = 0,
1884 width = 100,
1885 height = 100;
1886 // difference between chrome and content size
1887 int32_t chromeWidth = 0,
1888 chromeHeight = 0;
1889 // whether the window size spec refers to chrome or content
1890 bool sizeChromeWidth = true,
1891 sizeChromeHeight = true;
1893 // get various interfaces for aDocShellItem, used throughout this method
1894 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
1895 aDocShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
1896 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(treeOwner));
1897 if (!treeOwnerAsWin) // we'll need this to actually size the docshell
1898 return;
1900 double openerZoom = 1.0;
1901 if (aParent) {
1902 nsCOMPtr<nsIDOMDocument> openerDoc;
1903 aParent->GetDocument(getter_AddRefs(openerDoc));
1904 if (openerDoc) {
1905 nsCOMPtr<nsIDocument> doc = do_QueryInterface(openerDoc);
1906 nsIPresShell* shell = doc->GetShell();
1907 if (shell) {
1908 nsPresContext* presContext = shell->GetPresContext();
1909 if (presContext) {
1910 openerZoom = presContext->GetFullZoom();
1911 }
1912 }
1913 }
1914 }
1916 double scale;
1917 treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
1919 /* The current position and size will be unchanged if not specified
1920 (and they fit entirely onscreen). Also, calculate the difference
1921 between chrome and content sizes on aDocShellItem's window.
1922 This latter point becomes important if chrome and content
1923 specifications are mixed in aFeatures, and when bringing the window
1924 back from too far off the right or bottom edges of the screen. */
1926 treeOwnerAsWin->GetPositionAndSize(&left, &top, &width, &height);
1927 left = NSToIntRound(left / scale);
1928 top = NSToIntRound(top / scale);
1929 width = NSToIntRound(width / scale);
1930 height = NSToIntRound(height / scale);
1931 { // scope shellWindow why not
1932 nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(aDocShellItem));
1933 if (shellWindow) {
1934 int32_t cox, coy;
1935 double shellScale;
1936 shellWindow->GetSize(&cox, &coy);
1937 shellWindow->GetUnscaledDevicePixelsPerCSSPixel(&shellScale);
1938 chromeWidth = width - NSToIntRound(cox / shellScale);
1939 chromeHeight = height - NSToIntRound(coy / shellScale);
1940 }
1941 }
1943 // Set up left/top
1944 if (aSizeSpec.mLeftSpecified) {
1945 left = NSToIntRound(aSizeSpec.mLeft * openerZoom);
1946 }
1948 if (aSizeSpec.mTopSpecified) {
1949 top = NSToIntRound(aSizeSpec.mTop * openerZoom);
1950 }
1952 // Set up width
1953 if (aSizeSpec.mOuterWidthSpecified) {
1954 if (!aSizeSpec.mUseDefaultWidth) {
1955 width = NSToIntRound(aSizeSpec.mOuterWidth * openerZoom);
1956 } // Else specified to default; just use our existing width
1957 }
1958 else if (aSizeSpec.mInnerWidthSpecified) {
1959 sizeChromeWidth = false;
1960 if (aSizeSpec.mUseDefaultWidth) {
1961 width = width - chromeWidth;
1962 } else {
1963 width = NSToIntRound(aSizeSpec.mInnerWidth * openerZoom);
1964 }
1965 }
1967 // Set up height
1968 if (aSizeSpec.mOuterHeightSpecified) {
1969 if (!aSizeSpec.mUseDefaultHeight) {
1970 height = NSToIntRound(aSizeSpec.mOuterHeight * openerZoom);
1971 } // Else specified to default; just use our existing height
1972 }
1973 else if (aSizeSpec.mInnerHeightSpecified) {
1974 sizeChromeHeight = false;
1975 if (aSizeSpec.mUseDefaultHeight) {
1976 height = height - chromeHeight;
1977 } else {
1978 height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom);
1979 }
1980 }
1982 bool positionSpecified = aSizeSpec.PositionSpecified();
1984 // Check security state for use in determing window dimensions
1985 bool enabled = false;
1986 if (nsContentUtils::IsCallerChrome()) {
1987 // Only enable special priveleges for chrome when chrome calls
1988 // open() on a chrome window
1989 nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(aParent));
1990 enabled = !aParent || chromeWin;
1991 }
1993 if (!enabled) {
1995 // Security check failed. Ensure all args meet minimum reqs.
1997 int32_t oldTop = top,
1998 oldLeft = left;
2000 // We'll also need the screen dimensions
2001 nsCOMPtr<nsIScreen> screen;
2002 nsCOMPtr<nsIScreenManager> screenMgr(do_GetService(
2003 "@mozilla.org/gfx/screenmanager;1"));
2004 if (screenMgr)
2005 screenMgr->ScreenForRect(left, top, width, height,
2006 getter_AddRefs(screen));
2007 if (screen) {
2008 int32_t screenLeft, screenTop, screenWidth, screenHeight;
2009 int32_t winWidth = width + (sizeChromeWidth ? 0 : chromeWidth),
2010 winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
2012 screen->GetAvailRectDisplayPix(&screenLeft, &screenTop,
2013 &screenWidth, &screenHeight);
2015 if (aSizeSpec.SizeSpecified()) {
2016 /* Unlike position, force size out-of-bounds check only if
2017 size actually was specified. Otherwise, intrinsically sized
2018 windows are broken. */
2019 if (height < 100) {
2020 height = 100;
2021 winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
2022 }
2023 if (winHeight > screenHeight) {
2024 height = screenHeight - (sizeChromeHeight ? 0 : chromeHeight);
2025 }
2026 if (width < 100) {
2027 width = 100;
2028 winWidth = width + (sizeChromeWidth ? 0 : chromeWidth);
2029 }
2030 if (winWidth > screenWidth) {
2031 width = screenWidth - (sizeChromeWidth ? 0 : chromeWidth);
2032 }
2033 }
2035 if (left + winWidth > screenLeft + screenWidth || left + winWidth < left) {
2036 left = screenLeft + screenWidth - winWidth;
2037 }
2038 if (left < screenLeft) {
2039 left = screenLeft;
2040 }
2041 if (top + winHeight > screenTop + screenHeight || top + winHeight < top) {
2042 top = screenTop + screenHeight - winHeight;
2043 }
2044 if (top < screenTop) {
2045 top = screenTop;
2046 }
2047 if (top != oldTop || left != oldLeft) {
2048 positionSpecified = true;
2049 }
2050 }
2051 }
2053 // size and position the window
2055 if (positionSpecified) {
2056 treeOwnerAsWin->SetPosition(left * scale, top * scale);
2057 // moving the window may have changed its scale factor
2058 treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
2059 }
2060 if (aSizeSpec.SizeSpecified()) {
2061 /* Prefer to trust the interfaces, which think in terms of pure
2062 chrome or content sizes. If we have a mix, use the chrome size
2063 adjusted by the chrome/content differences calculated earlier. */
2064 if (!sizeChromeWidth && !sizeChromeHeight) {
2065 treeOwner->SizeShellTo(aDocShellItem, width * scale, height * scale);
2066 }
2067 else {
2068 if (!sizeChromeWidth)
2069 width += chromeWidth;
2070 if (!sizeChromeHeight)
2071 height += chromeHeight;
2072 treeOwnerAsWin->SetSize(width * scale, height * scale, false);
2073 }
2074 }
2075 treeOwnerAsWin->SetVisibility(true);
2076 }
2078 void
2079 nsWindowWatcher::GetWindowTreeItem(nsIDOMWindow *inWindow,
2080 nsIDocShellTreeItem **outTreeItem)
2081 {
2082 *outTreeItem = 0;
2084 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(inWindow));
2085 if (window) {
2086 nsIDocShell *docshell = window->GetDocShell();
2087 if (docshell)
2088 CallQueryInterface(docshell, outTreeItem);
2089 }
2090 }
2092 void
2093 nsWindowWatcher::GetWindowTreeOwner(nsIDOMWindow *inWindow,
2094 nsIDocShellTreeOwner **outTreeOwner)
2095 {
2096 *outTreeOwner = 0;
2098 nsCOMPtr<nsIDocShellTreeItem> treeItem;
2099 GetWindowTreeItem(inWindow, getter_AddRefs(treeItem));
2100 if (treeItem)
2101 treeItem->GetTreeOwner(outTreeOwner);
2102 }
2104 JSContext *
2105 nsWindowWatcher::GetJSContextFromWindow(nsIDOMWindow *aWindow)
2106 {
2107 JSContext *cx = 0;
2109 if (aWindow) {
2110 nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aWindow));
2111 if (sgo) {
2112 nsIScriptContext *scx = sgo->GetContext();
2113 if (scx)
2114 cx = scx->GetNativeContext();
2115 }
2116 /* (off-topic note:) the nsIScriptContext can be retrieved by
2117 nsCOMPtr<nsIScriptContext> scx;
2118 nsJSUtils::GetDynamicScriptContext(cx, getter_AddRefs(scx));
2119 */
2120 }
2122 return cx;
2123 }