embedding/components/windowwatcher/src/nsWindowWatcher.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial