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