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