|
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/. */ |
|
6 |
|
7 //#define USEWEAKREFS // (haven't quite figured that out yet) |
|
8 |
|
9 #include "nsWindowWatcher.h" |
|
10 #include "nsAutoWindowStateHelper.h" |
|
11 |
|
12 #include "nsCRT.h" |
|
13 #include "nsNetUtil.h" |
|
14 #include "nsJSUtils.h" |
|
15 #include "plstr.h" |
|
16 |
|
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" |
|
63 |
|
64 #ifdef USEWEAKREFS |
|
65 #include "nsIWeakReference.h" |
|
66 #endif |
|
67 |
|
68 using namespace mozilla; |
|
69 |
|
70 /**************************************************************** |
|
71 ******************** nsWatcherWindowEntry ********************** |
|
72 ****************************************************************/ |
|
73 |
|
74 class nsWindowWatcher; |
|
75 |
|
76 struct nsWatcherWindowEntry { |
|
77 |
|
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() {} |
|
94 |
|
95 void InsertAfter(nsWatcherWindowEntry *inOlder); |
|
96 void Unlink(); |
|
97 void ReferenceSelf(); |
|
98 |
|
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 }; |
|
110 |
|
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 } |
|
124 |
|
125 void nsWatcherWindowEntry::Unlink() { |
|
126 |
|
127 mOlder->mYounger = mYounger; |
|
128 mYounger->mOlder = mOlder; |
|
129 ReferenceSelf(); |
|
130 } |
|
131 |
|
132 void nsWatcherWindowEntry::ReferenceSelf() { |
|
133 |
|
134 mYounger = this; |
|
135 mOlder = this; |
|
136 } |
|
137 |
|
138 /**************************************************************** |
|
139 ****************** nsWatcherWindowEnumerator ******************* |
|
140 ****************************************************************/ |
|
141 |
|
142 class nsWatcherWindowEnumerator : public nsISimpleEnumerator { |
|
143 |
|
144 public: |
|
145 nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher); |
|
146 virtual ~nsWatcherWindowEnumerator(); |
|
147 NS_IMETHOD HasMoreElements(bool *retval); |
|
148 NS_IMETHOD GetNext(nsISupports **retval); |
|
149 |
|
150 NS_DECL_ISUPPORTS |
|
151 |
|
152 private: |
|
153 friend class nsWindowWatcher; |
|
154 |
|
155 nsWatcherWindowEntry *FindNext(); |
|
156 void WindowRemoved(nsWatcherWindowEntry *inInfo); |
|
157 |
|
158 nsWindowWatcher *mWindowWatcher; |
|
159 nsWatcherWindowEntry *mCurrentPosition; |
|
160 }; |
|
161 |
|
162 NS_IMPL_ADDREF(nsWatcherWindowEnumerator) |
|
163 NS_IMPL_RELEASE(nsWatcherWindowEnumerator) |
|
164 NS_IMPL_QUERY_INTERFACE(nsWatcherWindowEnumerator, nsISimpleEnumerator) |
|
165 |
|
166 nsWatcherWindowEnumerator::nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher) |
|
167 : mWindowWatcher(inWatcher), |
|
168 mCurrentPosition(inWatcher->mOldestWindow) |
|
169 { |
|
170 mWindowWatcher->AddEnumerator(this); |
|
171 mWindowWatcher->AddRef(); |
|
172 } |
|
173 |
|
174 nsWatcherWindowEnumerator::~nsWatcherWindowEnumerator() |
|
175 { |
|
176 mWindowWatcher->RemoveEnumerator(this); |
|
177 mWindowWatcher->Release(); |
|
178 } |
|
179 |
|
180 NS_IMETHODIMP |
|
181 nsWatcherWindowEnumerator::HasMoreElements(bool *retval) |
|
182 { |
|
183 if (!retval) |
|
184 return NS_ERROR_INVALID_ARG; |
|
185 |
|
186 *retval = mCurrentPosition? true : false; |
|
187 return NS_OK; |
|
188 } |
|
189 |
|
190 NS_IMETHODIMP |
|
191 nsWatcherWindowEnumerator::GetNext(nsISupports **retval) |
|
192 { |
|
193 if (!retval) |
|
194 return NS_ERROR_INVALID_ARG; |
|
195 |
|
196 *retval = nullptr; |
|
197 |
|
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 } |
|
216 |
|
217 nsWatcherWindowEntry * |
|
218 nsWatcherWindowEnumerator::FindNext() |
|
219 { |
|
220 nsWatcherWindowEntry *info; |
|
221 |
|
222 if (!mCurrentPosition) |
|
223 return 0; |
|
224 |
|
225 info = mCurrentPosition->mYounger; |
|
226 return info == mWindowWatcher->mOldestWindow ? 0 : info; |
|
227 } |
|
228 |
|
229 // if a window is being removed adjust the iterator's current position |
|
230 void nsWatcherWindowEnumerator::WindowRemoved(nsWatcherWindowEntry *inInfo) { |
|
231 |
|
232 if (mCurrentPosition == inInfo) |
|
233 mCurrentPosition = mCurrentPosition != inInfo->mYounger ? |
|
234 inInfo->mYounger : 0; |
|
235 } |
|
236 |
|
237 /**************************************************************** |
|
238 *********************** nsWindowWatcher ************************ |
|
239 ****************************************************************/ |
|
240 |
|
241 NS_IMPL_ADDREF(nsWindowWatcher) |
|
242 NS_IMPL_RELEASE(nsWindowWatcher) |
|
243 NS_IMPL_QUERY_INTERFACE(nsWindowWatcher, |
|
244 nsIWindowWatcher, |
|
245 nsIPromptFactory, |
|
246 nsPIWindowWatcher) |
|
247 |
|
248 nsWindowWatcher::nsWindowWatcher() : |
|
249 mEnumeratorList(), |
|
250 mOldestWindow(0), |
|
251 mListLock("nsWindowWatcher.mListLock") |
|
252 { |
|
253 } |
|
254 |
|
255 nsWindowWatcher::~nsWindowWatcher() |
|
256 { |
|
257 // delete data |
|
258 while (mOldestWindow) |
|
259 RemoveWindow(mOldestWindow); |
|
260 } |
|
261 |
|
262 nsresult |
|
263 nsWindowWatcher::Init() |
|
264 { |
|
265 return NS_OK; |
|
266 } |
|
267 |
|
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 } |
|
284 |
|
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; |
|
291 |
|
292 return array.forget(); |
|
293 } |
|
294 |
|
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 } |
|
302 |
|
303 nsCOMPtr<nsIMutableArray> mutableArray = |
|
304 do_CreateInstance(NS_ARRAY_CONTRACTID); |
|
305 NS_ENSURE_TRUE(mutableArray, nullptr); |
|
306 |
|
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 } |
|
313 |
|
314 return mutableArray.forget(); |
|
315 } |
|
316 |
|
317 nsCOMPtr<nsIMutableArray> singletonArray = |
|
318 do_CreateInstance(NS_ARRAY_CONTRACTID); |
|
319 NS_ENSURE_TRUE(singletonArray, nullptr); |
|
320 |
|
321 nsresult rv = singletonArray->AppendElement(aArguments, /* aWeak = */ false); |
|
322 NS_ENSURE_SUCCESS(rv, nullptr); |
|
323 |
|
324 return singletonArray.forget(); |
|
325 } |
|
326 |
|
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); |
|
336 |
|
337 uint32_t argc = 0; |
|
338 if (argv) { |
|
339 argv->GetLength(&argc); |
|
340 } |
|
341 bool dialog = (argc != 0); |
|
342 |
|
343 return OpenWindowInternal(aParent, aUrl, aName, aFeatures, |
|
344 /* calledFromJS = */ false, dialog, |
|
345 /* navigate = */ true, argv, _retval); |
|
346 } |
|
347 |
|
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 {} |
|
359 |
|
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 |
|
366 |
|
367 bool mLeftSpecified; |
|
368 bool mTopSpecified; |
|
369 bool mOuterWidthSpecified; |
|
370 bool mOuterHeightSpecified; |
|
371 bool mInnerWidthSpecified; |
|
372 bool mInnerHeightSpecified; |
|
373 |
|
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; |
|
378 |
|
379 bool PositionSpecified() const { |
|
380 return mLeftSpecified || mTopSpecified; |
|
381 } |
|
382 |
|
383 bool SizeSpecified() const { |
|
384 return mOuterWidthSpecified || mOuterHeightSpecified || |
|
385 mInnerWidthSpecified || mInnerHeightSpecified; |
|
386 } |
|
387 }; |
|
388 |
|
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); |
|
401 |
|
402 uint32_t argc = 0; |
|
403 if (argv) { |
|
404 argv->GetLength(&argc); |
|
405 } |
|
406 |
|
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 } |
|
414 |
|
415 return OpenWindowInternal(aParent, aUrl, aName, aFeatures, |
|
416 aCalledFromScript, dialog, |
|
417 aNavigate, argv, _retval); |
|
418 } |
|
419 |
|
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; |
|
447 |
|
448 NS_ENSURE_ARG_POINTER(_retval); |
|
449 *_retval = 0; |
|
450 |
|
451 if (!nsContentUtils::IsSafeToRunScript()) { |
|
452 return NS_ERROR_FAILURE; |
|
453 } |
|
454 |
|
455 GetWindowTreeOwner(aParent, getter_AddRefs(parentTreeOwner)); |
|
456 |
|
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 } |
|
463 |
|
464 nameSpecified = false; |
|
465 if (aName) { |
|
466 CopyUTF8toUTF16(aName, name); |
|
467 nameSpecified = true; |
|
468 } |
|
469 |
|
470 featuresSpecified = false; |
|
471 if (aFeatures) { |
|
472 features.Assign(aFeatures); |
|
473 featuresSpecified = true; |
|
474 features.StripWhitespace(); |
|
475 } |
|
476 |
|
477 // try to find an extant window with the given name |
|
478 nsCOMPtr<nsIDOMWindow> foundWindow = SafeGetWindowByName(name, aParent); |
|
479 GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem)); |
|
480 |
|
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 } |
|
495 |
|
496 // no extant window? make a new one. |
|
497 |
|
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 } |
|
507 |
|
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); |
|
515 |
|
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; |
|
522 |
|
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 } |
|
531 |
|
532 SizeSpec sizeSpec; |
|
533 CalcSizeSpec(features.get(), sizeSpec); |
|
534 |
|
535 nsCOMPtr<nsIScriptSecurityManager> |
|
536 sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID)); |
|
537 |
|
538 bool isCallerChrome = nsContentUtils::IsCallerChrome(); |
|
539 |
|
540 JSContext *cx = GetJSContextFromWindow(aParent); |
|
541 |
|
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. |
|
557 |
|
558 callerContextGuard.Push(cx); |
|
559 } |
|
560 |
|
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; |
|
567 |
|
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); |
|
574 |
|
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 } |
|
583 |
|
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!"); |
|
595 |
|
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)); |
|
602 |
|
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 } |
|
626 |
|
627 bool newWindowShouldBeModal = false; |
|
628 bool parentIsModal = false; |
|
629 if (!newDocShellItem) { |
|
630 windowIsNew = true; |
|
631 isNewToplevelWindow = true; |
|
632 |
|
633 nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner)); |
|
634 |
|
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 } |
|
642 |
|
643 if (weAreModal) { |
|
644 windowIsModal = true; |
|
645 // in case we added this because weAreModal |
|
646 chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL | |
|
647 nsIWebBrowserChrome::CHROME_DEPENDENT; |
|
648 } |
|
649 |
|
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 } |
|
666 |
|
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; |
|
672 |
|
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; |
|
684 |
|
685 // is the parent under popup conditions? |
|
686 if (parentWindow) { |
|
687 popupConditions = parentWindow->IsLoadingOrRunningTimeout(); |
|
688 } |
|
689 |
|
690 // chrome is always allowed, so clear the flag if the opener is chrome |
|
691 if (popupConditions) { |
|
692 popupConditions = !isCallerChrome; |
|
693 } |
|
694 |
|
695 if (popupConditions) |
|
696 contextFlags |= nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT; |
|
697 |
|
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 } |
|
725 |
|
726 // better have a window to use by this point |
|
727 if (!newDocShellItem) |
|
728 return rv; |
|
729 |
|
730 nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem)); |
|
731 NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED); |
|
732 |
|
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 } |
|
742 |
|
743 rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, windowIsNew, _retval); |
|
744 if (NS_FAILED(rv)) |
|
745 return rv; |
|
746 |
|
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. */ |
|
756 |
|
757 if (PL_strcasestr(features.get(), "width=") || PL_strcasestr(features.get(), "height=")) { |
|
758 |
|
759 nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner; |
|
760 newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner)); |
|
761 if (newTreeOwner) |
|
762 newTreeOwner->SetPersistence(false, false, false); |
|
763 } |
|
764 } |
|
765 |
|
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); |
|
770 |
|
771 rv = piwin->SetArguments(argv); |
|
772 NS_ENSURE_SUCCESS(rv, rv); |
|
773 } |
|
774 |
|
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 } |
|
785 |
|
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 } |
|
797 |
|
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 } |
|
817 |
|
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)); |
|
825 |
|
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 } |
|
837 |
|
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); |
|
842 |
|
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 } |
|
858 |
|
859 nsCOMPtr<nsIDocShellLoadInfo> loadInfo; |
|
860 if (uriToLoad && aNavigate) { // get the script principal and pass it to docshell |
|
861 |
|
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 } |
|
879 |
|
880 newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo)); |
|
881 NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE); |
|
882 |
|
883 if (subjectPrincipal) { |
|
884 loadInfo->SetOwner(subjectPrincipal); |
|
885 } |
|
886 |
|
887 // Set the new window's referrer from the calling context's document: |
|
888 |
|
889 // get its document, if any |
|
890 JSContext* ccx = nsContentUtils::GetCurrentJSContext(); |
|
891 if (ccx) { |
|
892 nsIScriptGlobalObject *sgo = nsJSUtils::GetDynamicScriptGlobal(ccx); |
|
893 |
|
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 } |
|
910 |
|
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 } |
|
919 |
|
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 } |
|
928 |
|
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); |
|
933 |
|
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 } |
|
942 |
|
943 if (isNewToplevelWindow) |
|
944 SizeOpenedDocShellItem(newDocShellItem, aParent, sizeSpec); |
|
945 |
|
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)); |
|
951 |
|
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); |
|
955 |
|
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); |
|
965 |
|
966 if (!windowStateHelper.DefaultEnabled()) { |
|
967 // Default to cancel not opening the modal window. |
|
968 NS_RELEASE(*_retval); |
|
969 |
|
970 return NS_OK; |
|
971 } |
|
972 |
|
973 |
|
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); |
|
988 |
|
989 newChrome->ShowAsModal(); |
|
990 } |
|
991 } |
|
992 |
|
993 return NS_OK; |
|
994 } |
|
995 |
|
996 NS_IMETHODIMP |
|
997 nsWindowWatcher::RegisterNotification(nsIObserver *aObserver) |
|
998 { |
|
999 // just a convenience method; it delegates to nsIObserverService |
|
1000 |
|
1001 if (!aObserver) |
|
1002 return NS_ERROR_INVALID_ARG; |
|
1003 |
|
1004 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
1005 if (!os) |
|
1006 return NS_ERROR_FAILURE; |
|
1007 |
|
1008 nsresult rv = os->AddObserver(aObserver, "domwindowopened", false); |
|
1009 if (NS_SUCCEEDED(rv)) |
|
1010 rv = os->AddObserver(aObserver, "domwindowclosed", false); |
|
1011 |
|
1012 return rv; |
|
1013 } |
|
1014 |
|
1015 NS_IMETHODIMP |
|
1016 nsWindowWatcher::UnregisterNotification(nsIObserver *aObserver) |
|
1017 { |
|
1018 // just a convenience method; it delegates to nsIObserverService |
|
1019 |
|
1020 if (!aObserver) |
|
1021 return NS_ERROR_INVALID_ARG; |
|
1022 |
|
1023 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
1024 if (!os) |
|
1025 return NS_ERROR_FAILURE; |
|
1026 |
|
1027 os->RemoveObserver(aObserver, "domwindowopened"); |
|
1028 os->RemoveObserver(aObserver, "domwindowclosed"); |
|
1029 |
|
1030 return NS_OK; |
|
1031 } |
|
1032 |
|
1033 NS_IMETHODIMP |
|
1034 nsWindowWatcher::GetWindowEnumerator(nsISimpleEnumerator** _retval) |
|
1035 { |
|
1036 if (!_retval) |
|
1037 return NS_ERROR_INVALID_ARG; |
|
1038 |
|
1039 MutexAutoLock lock(mListLock); |
|
1040 nsWatcherWindowEnumerator *enumerator = new nsWatcherWindowEnumerator(this); |
|
1041 if (enumerator) |
|
1042 return CallQueryInterface(enumerator, _retval); |
|
1043 |
|
1044 return NS_ERROR_OUT_OF_MEMORY; |
|
1045 } |
|
1046 |
|
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 } |
|
1056 |
|
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 } |
|
1066 |
|
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); |
|
1076 |
|
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); |
|
1084 |
|
1085 NS_WrapAuthPrompt(oldPrompt, reinterpret_cast<nsIAuthPrompt2**>(_retval)); |
|
1086 if (!*_retval) |
|
1087 rv = NS_ERROR_NOT_AVAILABLE; |
|
1088 } |
|
1089 return rv; |
|
1090 } |
|
1091 |
|
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 } |
|
1098 |
|
1099 NS_IMETHODIMP |
|
1100 nsWindowWatcher::HasWindowCreator(bool *result) |
|
1101 { |
|
1102 *result = mWindowCreator; |
|
1103 return NS_OK; |
|
1104 } |
|
1105 |
|
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 } |
|
1115 |
|
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 } |
|
1124 |
|
1125 NS_IMETHODIMP |
|
1126 nsWindowWatcher::AddWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome *aChrome) |
|
1127 { |
|
1128 if (!aWindow) |
|
1129 return NS_ERROR_INVALID_ARG; |
|
1130 |
|
1131 #ifdef DEBUG |
|
1132 { |
|
1133 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aWindow)); |
|
1134 |
|
1135 NS_ASSERTION(win->IsOuterWindow(), |
|
1136 "Uh, the active window must be an outer window!"); |
|
1137 } |
|
1138 #endif |
|
1139 |
|
1140 { |
|
1141 nsWatcherWindowEntry *info; |
|
1142 MutexAutoLock lock(mListLock); |
|
1143 |
|
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 } |
|
1157 |
|
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; |
|
1162 |
|
1163 if (mOldestWindow) |
|
1164 info->InsertAfter(mOldestWindow->mOlder); |
|
1165 else |
|
1166 mOldestWindow = info; |
|
1167 } // leave the mListLock |
|
1168 |
|
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; |
|
1174 |
|
1175 nsCOMPtr<nsISupports> domwin(do_QueryInterface(aWindow)); |
|
1176 return os->NotifyObservers(domwin, "domwindowopened", 0); |
|
1177 } |
|
1178 |
|
1179 NS_IMETHODIMP |
|
1180 nsWindowWatcher::RemoveWindow(nsIDOMWindow *aWindow) |
|
1181 { |
|
1182 // find the corresponding nsWatcherWindowEntry, remove it |
|
1183 |
|
1184 if (!aWindow) |
|
1185 return NS_ERROR_INVALID_ARG; |
|
1186 |
|
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 } |
|
1195 |
|
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 |
|
1206 |
|
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; |
|
1219 |
|
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 } |
|
1234 |
|
1235 nsresult nsWindowWatcher::RemoveWindow(nsWatcherWindowEntry *inInfo) |
|
1236 { |
|
1237 uint32_t ctr, |
|
1238 count = mEnumeratorList.Length(); |
|
1239 |
|
1240 { |
|
1241 // notify the enumerators |
|
1242 MutexAutoLock lock(mListLock); |
|
1243 for (ctr = 0; ctr < count; ++ctr) |
|
1244 mEnumeratorList[ctr]->WindowRemoved(inInfo); |
|
1245 |
|
1246 // remove the element from the list |
|
1247 if (inInfo == mOldestWindow) |
|
1248 mOldestWindow = inInfo->mYounger == mOldestWindow ? 0 : inInfo->mYounger; |
|
1249 inInfo->Unlink(); |
|
1250 } |
|
1251 |
|
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 } |
|
1266 |
|
1267 delete inInfo; |
|
1268 return NS_OK; |
|
1269 } |
|
1270 |
|
1271 NS_IMETHODIMP |
|
1272 nsWindowWatcher::GetChromeForWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome **_retval) |
|
1273 { |
|
1274 if (!aWindow || !_retval) |
|
1275 return NS_ERROR_INVALID_ARG; |
|
1276 *_retval = 0; |
|
1277 |
|
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 } |
|
1291 |
|
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 } |
|
1300 |
|
1301 *aResult = nullptr; |
|
1302 |
|
1303 nsCOMPtr<nsIDocShellTreeItem> treeItem; |
|
1304 |
|
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 } |
|
1316 |
|
1317 nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(treeItem); |
|
1318 domWindow.swap(*aResult); |
|
1319 |
|
1320 return NS_OK; |
|
1321 } |
|
1322 |
|
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 } |
|
1329 |
|
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 } |
|
1336 |
|
1337 nsresult |
|
1338 nsWindowWatcher::URIfromURL(const char *aURL, |
|
1339 nsIDOMWindow *aParent, |
|
1340 nsIURI **aURI) |
|
1341 { |
|
1342 nsCOMPtr<nsIDOMWindow> baseWindow; |
|
1343 |
|
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 } |
|
1354 |
|
1355 // failing that, build it relative to the parent window, if possible |
|
1356 if (!baseWindow) |
|
1357 baseWindow = aParent; |
|
1358 |
|
1359 // failing that, use the given URL unmodified. It had better not be relative. |
|
1360 |
|
1361 nsIURI *baseURI = nullptr; |
|
1362 |
|
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 } |
|
1375 |
|
1376 // build and return the absolute URI |
|
1377 return NS_NewURI(aURI, aURL, baseURI); |
|
1378 } |
|
1379 |
|
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 } |
|
1390 |
|
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 } |
|
1416 |
|
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. */ |
|
1424 |
|
1425 uint32_t chromeFlags = 0; |
|
1426 bool presenceFlag = false; |
|
1427 |
|
1428 chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS; |
|
1429 if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag)) |
|
1430 chromeFlags = nsIWebBrowserChrome::CHROME_ALL; |
|
1431 |
|
1432 /* Next, allow explicitly named options to override the initial settings */ |
|
1433 |
|
1434 bool isCallerChrome = nsContentUtils::IsCallerChrome(); |
|
1435 |
|
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 } |
|
1443 |
|
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 } |
|
1456 |
|
1457 nsresult rv; |
|
1458 |
|
1459 nsCOMPtr<nsIPrefBranch> prefBranch; |
|
1460 nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); |
|
1461 NS_ENSURE_SUCCESS(rv, true); |
|
1462 |
|
1463 rv = prefs->GetBranch("dom.disable_window_open_feature.", getter_AddRefs(prefBranch)); |
|
1464 NS_ENSURE_SUCCESS(rv, true); |
|
1465 |
|
1466 bool forceEnable = false; |
|
1467 |
|
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); |
|
1488 |
|
1489 chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag) |
|
1490 ? nsIWebBrowserChrome::CHROME_WINDOW_POPUP : 0; |
|
1491 |
|
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." */ |
|
1498 |
|
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 } |
|
1506 |
|
1507 if (aDialog && !presenceFlag) |
|
1508 chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT; |
|
1509 |
|
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.) */ |
|
1513 |
|
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; |
|
1519 |
|
1520 chromeFlags |= WinHasOption(aFeatures, "macsuppressanimation", 0, nullptr) ? |
|
1521 nsIWebBrowserChrome::CHROME_MAC_SUPPRESS_ANIMATION : 0; |
|
1522 |
|
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; |
|
1533 |
|
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); |
|
1540 |
|
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 } |
|
1550 |
|
1551 if (!disableDialogFeature) { |
|
1552 chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr) ? |
|
1553 nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0; |
|
1554 } |
|
1555 |
|
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 } |
|
1564 |
|
1565 /* missing |
|
1566 chromeFlags->copy_history |
|
1567 */ |
|
1568 |
|
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 } |
|
1587 |
|
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 } |
|
1592 |
|
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 } |
|
1599 |
|
1600 return chromeFlags; |
|
1601 } |
|
1602 |
|
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; |
|
1610 |
|
1611 char *comma, *equal; |
|
1612 int32_t found = 0; |
|
1613 |
|
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 |
|
1619 |
|
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 } |
|
1650 |
|
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; |
|
1662 |
|
1663 /* special cases */ |
|
1664 if(!aName || !*aName) |
|
1665 return NS_OK; |
|
1666 |
|
1667 nsDependentString name(aName); |
|
1668 |
|
1669 nsCOMPtr<nsISimpleEnumerator> windows; |
|
1670 GetWindowEnumerator(getter_AddRefs(windows)); |
|
1671 if (!windows) |
|
1672 return NS_ERROR_FAILURE; |
|
1673 |
|
1674 bool more; |
|
1675 nsresult rv = NS_OK; |
|
1676 |
|
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); |
|
1715 |
|
1716 return rv; |
|
1717 } |
|
1718 |
|
1719 already_AddRefed<nsIDocShellTreeItem> |
|
1720 nsWindowWatcher::GetCallerTreeItem(nsIDocShellTreeItem* aParentItem) |
|
1721 { |
|
1722 JSContext *cx = nsContentUtils::GetCurrentJSContext(); |
|
1723 nsCOMPtr<nsIDocShellTreeItem> callerItem; |
|
1724 |
|
1725 if (cx) { |
|
1726 nsCOMPtr<nsIWebNavigation> callerWebNav = |
|
1727 do_GetInterface(nsJSUtils::GetDynamicScriptGlobal(cx)); |
|
1728 |
|
1729 callerItem = do_QueryInterface(callerWebNav); |
|
1730 } |
|
1731 |
|
1732 if (!callerItem) { |
|
1733 callerItem = aParentItem; |
|
1734 } |
|
1735 |
|
1736 return callerItem.forget(); |
|
1737 } |
|
1738 |
|
1739 already_AddRefed<nsIDOMWindow> |
|
1740 nsWindowWatcher::SafeGetWindowByName(const nsAString& aName, |
|
1741 nsIDOMWindow* aCurrentWindow) |
|
1742 { |
|
1743 nsCOMPtr<nsIDocShellTreeItem> startItem; |
|
1744 GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem)); |
|
1745 |
|
1746 nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(startItem); |
|
1747 |
|
1748 const nsAFlatString& flatName = PromiseFlatString(aName); |
|
1749 |
|
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 } |
|
1759 |
|
1760 nsCOMPtr<nsIDOMWindow> foundWin = do_GetInterface(foundItem); |
|
1761 return foundWin.forget(); |
|
1762 } |
|
1763 |
|
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; |
|
1776 |
|
1777 *aOpenedWindow = 0; |
|
1778 nsCOMPtr<nsPIDOMWindow> piOpenedWindow(do_GetInterface(aOpenedItem)); |
|
1779 if (piOpenedWindow) { |
|
1780 if (aParent) { |
|
1781 piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit |
|
1782 |
|
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?"); |
|
1791 |
|
1792 nsCOMPtr<nsIChannel> chan; |
|
1793 docloader->GetDocumentChannel(getter_AddRefs(chan)); |
|
1794 NS_ASSERTION(!chan, "Why is there a document channel?"); |
|
1795 #endif |
|
1796 |
|
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 } |
|
1807 |
|
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; |
|
1815 |
|
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; |
|
1822 |
|
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; |
|
1829 |
|
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 } |
|
1849 |
|
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 } |
|
1870 |
|
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; |
|
1892 |
|
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; |
|
1899 |
|
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 } |
|
1915 |
|
1916 double scale; |
|
1917 treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale); |
|
1918 |
|
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. */ |
|
1925 |
|
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 } |
|
1942 |
|
1943 // Set up left/top |
|
1944 if (aSizeSpec.mLeftSpecified) { |
|
1945 left = NSToIntRound(aSizeSpec.mLeft * openerZoom); |
|
1946 } |
|
1947 |
|
1948 if (aSizeSpec.mTopSpecified) { |
|
1949 top = NSToIntRound(aSizeSpec.mTop * openerZoom); |
|
1950 } |
|
1951 |
|
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 } |
|
1966 |
|
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 } |
|
1981 |
|
1982 bool positionSpecified = aSizeSpec.PositionSpecified(); |
|
1983 |
|
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 } |
|
1992 |
|
1993 if (!enabled) { |
|
1994 |
|
1995 // Security check failed. Ensure all args meet minimum reqs. |
|
1996 |
|
1997 int32_t oldTop = top, |
|
1998 oldLeft = left; |
|
1999 |
|
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); |
|
2011 |
|
2012 screen->GetAvailRectDisplayPix(&screenLeft, &screenTop, |
|
2013 &screenWidth, &screenHeight); |
|
2014 |
|
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 } |
|
2034 |
|
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 } |
|
2052 |
|
2053 // size and position the window |
|
2054 |
|
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 } |
|
2077 |
|
2078 void |
|
2079 nsWindowWatcher::GetWindowTreeItem(nsIDOMWindow *inWindow, |
|
2080 nsIDocShellTreeItem **outTreeItem) |
|
2081 { |
|
2082 *outTreeItem = 0; |
|
2083 |
|
2084 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(inWindow)); |
|
2085 if (window) { |
|
2086 nsIDocShell *docshell = window->GetDocShell(); |
|
2087 if (docshell) |
|
2088 CallQueryInterface(docshell, outTreeItem); |
|
2089 } |
|
2090 } |
|
2091 |
|
2092 void |
|
2093 nsWindowWatcher::GetWindowTreeOwner(nsIDOMWindow *inWindow, |
|
2094 nsIDocShellTreeOwner **outTreeOwner) |
|
2095 { |
|
2096 *outTreeOwner = 0; |
|
2097 |
|
2098 nsCOMPtr<nsIDocShellTreeItem> treeItem; |
|
2099 GetWindowTreeItem(inWindow, getter_AddRefs(treeItem)); |
|
2100 if (treeItem) |
|
2101 treeItem->GetTreeOwner(outTreeOwner); |
|
2102 } |
|
2103 |
|
2104 JSContext * |
|
2105 nsWindowWatcher::GetJSContextFromWindow(nsIDOMWindow *aWindow) |
|
2106 { |
|
2107 JSContext *cx = 0; |
|
2108 |
|
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 } |
|
2121 |
|
2122 return cx; |
|
2123 } |