/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=2 et tw=78: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Travis Bogard * Brendan Eich * David Hyatt (hyatt@netscape.com) * Dan Rosen * Vidur Apparao * Johnny Stenback * Mark Hammond * Ryan Jones * Jeff Walden * Ben Bucksch * Emanuele Costa * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "base/basictypes.h" /* This must occur *after* base/basictypes.h to avoid typedefs conflicts. */ #include "mozilla/Util.h" // Local Includes #include "nsGlobalWindow.h" #include "Navigator.h" #include "nsScreen.h" #include "nsHistory.h" #include "nsPerformance.h" #include "nsDOMNavigationTiming.h" #include "nsBarProps.h" #include "nsDOMStorage.h" #include "nsDOMOfflineResourceList.h" #include "nsDOMError.h" #ifdef XP_WIN #ifdef GetClassName #undef GetClassName #endif // GetClassName #endif // XP_WIN // Helper Classes #include "nsXPIDLString.h" #include "nsJSUtils.h" #include "prmem.h" #include "jsapi.h" // for JSAutoRequest #include "jsdbgapi.h" // for JS_ClearWatchPointsForObject #include "jsfriendapi.h" // for JS_GetGlobalForFrame #include "jswrapper.h" #include "nsReadableUtils.h" #include "nsDOMClassInfo.h" #include "nsJSEnvironment.h" #include "nsCharSeparatedTokenizer.h" // for Accept-Language parsing #include "nsUnicharUtils.h" #include "mozilla/Preferences.h" // Other Classes #include "nsEventListenerManager.h" #include "nsEscape.h" #include "nsStyleCoord.h" #include "nsMimeTypeArray.h" #include "nsNetUtil.h" #include "nsICachingChannel.h" #include "nsPluginArray.h" #include "nsIPluginHost.h" #include "nsPluginHost.h" #include "nsIPluginInstanceOwner.h" #include "nsGeolocation.h" #include "nsDesktopNotification.h" #include "nsContentCID.h" #include "nsLayoutStatics.h" #include "nsCycleCollector.h" #include "nsCCUncollectableMarker.h" #include "nsAutoJSValHolder.h" #include "nsDOMMediaQueryList.h" #include "mozilla/dom/workers/Workers.h" // Interfaces Needed #include "nsIFrame.h" #include "nsCanvasFrame.h" #include "nsIWidget.h" #include "nsIBaseWindow.h" #include "nsDeviceSensors.h" #include "nsIContent.h" #include "nsIContentViewerEdit.h" #include "nsIDocShell.h" #include "nsIDocShellLoadInfo.h" #include "nsIDocShellTreeItem.h" #include "nsIDocShellTreeNode.h" #include "nsIEditorDocShell.h" #include "nsIDocCharset.h" #include "nsIDocument.h" #include "nsIHTMLDocument.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLElement.h" #ifndef MOZ_DISABLE_DOMCRYPTO #include "nsIDOMCrypto.h" #endif #include "nsIDOMDocument.h" #include "nsIDOMElement.h" #include "nsIDOMEvent.h" #include "nsIDOMHTMLAnchorElement.h" #include "nsIDOMKeyEvent.h" #include "nsIDOMMessageEvent.h" #include "nsIDOMPopupBlockedEvent.h" #include "nsIDOMPopStateEvent.h" #include "nsIDOMHashChangeEvent.h" #include "nsIDOMOfflineResourceList.h" #include "nsIDOMGeoGeolocation.h" #include "nsIDOMDesktopNotification.h" #include "nsPIDOMStorage.h" #include "nsDOMString.h" #include "nsIEmbeddingSiteWindow2.h" #include "nsThreadUtils.h" #include "nsEventStateManager.h" #include "nsIHttpProtocolHandler.h" #include "nsIJSContextStack.h" #include "nsIJSRuntimeService.h" #include "nsIMarkupDocumentViewer.h" #include "nsIPresShell.h" #include "nsIPrivateDOMEvent.h" #include "nsIProgrammingLanguage.h" #include "nsIServiceManager.h" #include "nsIScriptGlobalObjectOwner.h" #include "nsIScriptSecurityManager.h" #include "nsIScrollableFrame.h" #include "nsIView.h" #include "nsIViewManager.h" #include "nsISelectionController.h" #include "nsISelection.h" #include "nsIPrompt.h" #include "nsIPromptService.h" #include "nsIPromptFactory.h" #include "nsIWritablePropertyBag2.h" #include "nsIWebNavigation.h" #include "nsIWebBrowser.h" #include "nsIWebBrowserChrome.h" #include "nsIWebBrowserFind.h" // For window.find() #include "nsIWebContentHandlerRegistrar.h" #include "nsIWindowMediator.h" // For window.find() #include "nsComputedDOMStyle.h" #include "nsIEntropyCollector.h" #include "nsDOMCID.h" #include "nsDOMError.h" #include "nsDOMWindowUtils.h" #include "nsIWindowWatcher.h" #include "nsPIWindowWatcher.h" #include "nsIContentViewer.h" #include "nsIJSNativeInitializer.h" #include "nsIScriptError.h" #include "nsIConsoleService.h" #include "nsIControllers.h" #include "nsIControllerContext.h" #include "nsGlobalWindowCommands.h" #include "nsAutoPtr.h" #include "nsContentUtils.h" #include "nsCSSProps.h" #include "nsBlobProtocolHandler.h" #include "nsIDOMFile.h" #include "nsIDOMFileList.h" #include "nsIURIFixup.h" #include "mozilla/FunctionTimer.h" #include "nsCDefaultURIFixup.h" #include "nsEventDispatcher.h" #include "nsIObserverService.h" #include "nsIXULAppInfo.h" #include "nsNetUtil.h" #include "nsFocusManager.h" #include "nsIXULWindow.h" #include "nsEventStateManager.h" #include "nsITimedChannel.h" #include "nsICookiePermission.h" #include "nsServiceManagerUtils.h" #ifdef MOZ_XUL #include "nsXULPopupManager.h" #include "nsIDOMXULControlElement.h" #include "nsMenuPopupFrame.h" #endif #include "xpcprivate.h" #ifdef NS_PRINTING #include "nsIPrintSettings.h" #include "nsIPrintSettingsService.h" #include "nsIWebBrowserPrint.h" #endif #include "nsWindowRoot.h" #include "nsNetCID.h" #include "nsIArray.h" #include "nsIScriptRuntime.h" // XXX An unfortunate dependency exists here (two XUL files). #include "nsIDOMXULDocument.h" #include "nsIDOMXULCommandDispatcher.h" #include "nsBindingManager.h" #include "nsIXBLService.h" // used for popup blocking, needs to be converted to something // belonging to the back-end like nsIContentPolicy #include "nsIPopupWindowManager.h" #include "nsIDragService.h" #include "mozilla/dom/Element.h" #include "nsFrameLoader.h" #include "nsISupportsPrimitives.h" #include "nsXPCOMCID.h" #include "mozilla/FunctionTimer.h" #include "mozIThirdPartyUtil.h" #ifdef MOZ_LOGGING // so we can get logging even in release builds #define FORCE_PR_LOG 1 #endif #include "prlog.h" #include "prenv.h" #include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" #include "mozilla/dom/StructuredCloneTags.h" #include "nsRefreshDriver.h" #include "mozAutoDocUpdate.h" #include "mozilla/Telemetry.h" #include "nsLocation.h" #include "nsWrapperCacheInlines.h" #include "nsDOMEventTargetHelper.h" #ifdef ANDROID #include #endif #ifdef PR_LOGGING static PRLogModuleInfo* gDOMLeakPRLog; #endif static const char kStorageEnabled[] = "dom.storage.enabled"; using namespace mozilla; using namespace mozilla::dom; using mozilla::TimeStamp; using mozilla::TimeDuration; nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nsnull; bool nsGlobalWindow::sWarnedAboutWindowInternal = false; static nsIEntropyCollector *gEntropyCollector = nsnull; static PRInt32 gRefCnt = 0; static PRInt32 gOpenPopupSpamCount = 0; static PopupControlState gPopupControlState = openAbused; static PRInt32 gRunningTimeoutDepth = 0; static bool gMouseDown = false; static bool gDragServiceDisabled = false; static FILE *gDumpFile = nsnull; static PRUint64 gNextWindowID = 0; static PRUint32 gSerialCounter = 0; static PRUint32 gTimeoutsRecentlySet = 0; static TimeStamp gLastRecordedRecentTimeouts; #define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC) #ifdef DEBUG_jst PRInt32 gTimeoutCnt = 0; #endif #if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP)) static bool gDOMWindowDumpEnabled = false; #endif #if defined(DEBUG_bryner) || defined(DEBUG_chb) #define DEBUG_PAGE_CACHE #endif #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added" // The default shortest interval/timeout we permit #define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms #define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms static PRInt32 gMinTimeoutValue; static PRInt32 gMinBackgroundTimeoutValue; inline PRInt32 nsGlobalWindow::DOMMinTimeoutValue() const { bool isBackground = !mOuterWindow || mOuterWindow->IsBackground(); return NS_MAX(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, 0); } // The number of nested timeouts before we start clamping. HTML5 says 1, WebKit // uses 5. #define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5 // The longest interval (as PRIntervalTime) we permit, or that our // timer code can handle, really. See DELAY_INTERVAL_LIMIT in // nsTimerImpl.h for details. #define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT #define FORWARD_TO_OUTER(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ nsGlobalWindow *outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return err_rval; \ } \ return outer->method args; \ } \ PR_END_MACRO #define FORWARD_TO_OUTER_VOID(method, args) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ nsGlobalWindow *outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return; \ } \ outer->method args; \ return; \ } \ PR_END_MACRO #define FORWARD_TO_OUTER_CHROME(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ nsGlobalWindow *outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return err_rval; \ } \ return ((nsGlobalChromeWindow *)outer)->method args; \ } \ PR_END_MACRO #define FORWARD_TO_INNER_CHROME(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsOuterWindow()) { \ if (!mInnerWindow) { \ NS_WARNING("No inner window available!"); \ return err_rval; \ } \ return ((nsGlobalChromeWindow *)mInnerWindow)->method args; \ } \ PR_END_MACRO #define FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsInnerWindow()) { \ nsGlobalWindow *outer = GetOuterWindowInternal(); \ if (!outer) { \ NS_WARNING("No outer window available!"); \ return err_rval; \ } \ return ((nsGlobalModalWindow *)outer)->method args; \ } \ PR_END_MACRO #define FORWARD_TO_INNER(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsOuterWindow()) { \ if (!mInnerWindow) { \ NS_WARNING("No inner window available!"); \ return err_rval; \ } \ return GetCurrentInnerWindowInternal()->method args; \ } \ PR_END_MACRO #define FORWARD_TO_INNER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsOuterWindow()) { \ if (!mInnerWindow) { \ NS_WARNING("No inner window available!"); \ return err_rval; \ } \ return ((nsGlobalModalWindow*)GetCurrentInnerWindowInternal())->method args; \ } \ PR_END_MACRO #define FORWARD_TO_INNER_VOID(method, args) \ PR_BEGIN_MACRO \ if (IsOuterWindow()) { \ if (!mInnerWindow) { \ NS_WARNING("No inner window available!"); \ return; \ } \ GetCurrentInnerWindowInternal()->method args; \ return; \ } \ PR_END_MACRO // Same as FORWARD_TO_INNER, but this will create a fresh inner if an // inner doesn't already exists. #define FORWARD_TO_INNER_CREATE(method, args, err_rval) \ PR_BEGIN_MACRO \ if (IsOuterWindow()) { \ if (!mInnerWindow) { \ if (mIsClosed) { \ return err_rval; \ } \ nsCOMPtr doc; \ nsresult fwdic_nr = GetDocument(getter_AddRefs(doc)); \ NS_ENSURE_SUCCESS(fwdic_nr, err_rval); \ if (!mInnerWindow) { \ return err_rval; \ } \ } \ return GetCurrentInnerWindowInternal()->method args; \ } \ PR_END_MACRO // CIDs static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID); static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1"; #ifndef MOZ_DISABLE_DOMCRYPTO static const char kCryptoContractID[] = NS_CRYPTO_CONTRACTID; static const char kPkcs11ContractID[] = NS_PKCS11_CONTRACTID; #endif static const char sPopStatePrefStr[] = "browser.history.allowPopState"; /** * An object implementing the window.URL property. */ class nsDOMMozURLProperty : public nsIDOMMozURLProperty { public: nsDOMMozURLProperty(nsGlobalWindow* aWindow) : mWindow(aWindow) { } NS_DECL_ISUPPORTS NS_DECL_NSIDOMMOZURLPROPERTY void ClearWindowReference() { mWindow = nsnull; } private: nsGlobalWindow* mWindow; }; DOMCI_DATA(MozURLProperty, nsDOMMozURLProperty) NS_IMPL_ADDREF(nsDOMMozURLProperty) NS_IMPL_RELEASE(nsDOMMozURLProperty) NS_INTERFACE_MAP_BEGIN(nsDOMMozURLProperty) NS_INTERFACE_MAP_ENTRY(nsIDOMMozURLProperty) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozURLProperty) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozURLProperty) NS_INTERFACE_MAP_END NS_IMETHODIMP nsDOMMozURLProperty::CreateObjectURL(nsIDOMBlob* aBlob, nsAString& aURL) { NS_PRECONDITION(!mWindow || mWindow->IsInnerWindow(), "Should be inner window"); NS_ENSURE_STATE(mWindow && mWindow->mDoc); NS_ENSURE_ARG_POINTER(aBlob); nsIDocument* doc = mWindow->mDoc; nsresult rv = aBlob->GetInternalUrl(doc->NodePrincipal(), aURL); NS_ENSURE_SUCCESS(rv, rv); doc->RegisterFileDataUri(NS_LossyConvertUTF16toASCII(aURL)); return NS_OK; } NS_IMETHODIMP nsDOMMozURLProperty::RevokeObjectURL(const nsAString& aURL) { NS_PRECONDITION(!mWindow || mWindow->IsInnerWindow(), "Should be inner window"); NS_ENSURE_STATE(mWindow); NS_LossyConvertUTF16toASCII asciiurl(aURL); nsIPrincipal* winPrincipal = mWindow->GetPrincipal(); if (!winPrincipal) { return NS_OK; } nsIPrincipal* principal = nsBlobProtocolHandler::GetFileDataEntryPrincipal(asciiurl); bool subsumes; if (principal && winPrincipal && NS_SUCCEEDED(winPrincipal->Subsumes(principal, &subsumes)) && subsumes) { if (mWindow->mDoc) { mWindow->mDoc->UnregisterFileDataUri(asciiurl); } nsBlobProtocolHandler::RemoveFileDataEntry(asciiurl); } return NS_OK; } /** * An indirect observer object that means we don't have to implement nsIObserver * on nsGlobalWindow, where any script could see it. */ class nsGlobalWindowObserver : public nsIObserver { public: nsGlobalWindowObserver(nsGlobalWindow* aWindow) : mWindow(aWindow) {} NS_DECL_ISUPPORTS NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { if (!mWindow) return NS_OK; return mWindow->Observe(aSubject, aTopic, aData); } void Forget() { mWindow = nsnull; } private: nsGlobalWindow* mWindow; }; NS_IMPL_ISUPPORTS1(nsGlobalWindowObserver, nsIObserver) nsTimeout::nsTimeout() { #ifdef DEBUG_jst { extern int gTimeoutCnt; ++gTimeoutCnt; } #endif memset(this, 0, sizeof(*this)); MOZ_COUNT_CTOR(nsTimeout); } nsTimeout::~nsTimeout() { #ifdef DEBUG_jst { extern int gTimeoutCnt; --gTimeoutCnt; } #endif MOZ_COUNT_DTOR(nsTimeout); } NS_IMPL_CYCLE_COLLECTION_CLASS(nsTimeout) NS_IMPL_CYCLE_COLLECTION_UNLINK_NATIVE_0(nsTimeout) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsTimeout) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mWindow, nsIScriptGlobalObject) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptHandler) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTimeout, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTimeout, Release) nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow) : mFrameElement(nsnull), mDocShell(nsnull), mModalStateDepth(0), mRunningTimeout(nsnull), mMutationBits(0), mIsDocumentLoaded(false), mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nsnull), mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false), mMayHaveMouseEnterLeaveEventListener(false), mIsModalContentWindow(false), mIsActive(false), mIsBackground(false), mInnerWindow(nsnull), mOuterWindow(aOuterWindow), // Make sure no actual window ends up with mWindowID == 0 mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false) {} nsPIDOMWindow::~nsPIDOMWindow() {} //***************************************************************************** // nsOuterWindowProxy: Outer Window Proxy //***************************************************************************** class nsOuterWindowProxy : public js::Wrapper { public: nsOuterWindowProxy() : js::Wrapper(0) {} virtual bool isOuterWindow() { return true; } JSString *obj_toString(JSContext *cx, JSObject *wrapper); void finalize(JSFreeOp *fop, JSObject *proxy); static nsOuterWindowProxy singleton; }; JSString * nsOuterWindowProxy::obj_toString(JSContext *cx, JSObject *proxy) { JS_ASSERT(js::IsProxy(proxy)); return JS_NewStringCopyZ(cx, "[object Window]"); } void nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy) { nsISupports *global = static_cast(js::GetProxyExtra(proxy, 0).toPrivate()); if (global) { nsWrapperCache *cache; CallQueryInterface(global, &cache); cache->ClearWrapper(); } } nsOuterWindowProxy nsOuterWindowProxy::singleton; static JSObject* NewOuterWindowProxy(JSContext *cx, JSObject *parent) { JSAutoEnterCompartment ac; if (!ac.enter(cx, parent)) { return nsnull; } JSObject *obj = js::Wrapper::New(cx, parent, js::GetObjectProto(parent), parent, &nsOuterWindowProxy::singleton); NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class"); return obj; } //***************************************************************************** //*** nsGlobalWindow: Object Management //***************************************************************************** nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) : nsPIDOMWindow(aOuterWindow), mIsFrozen(false), mFullScreen(false), mIsClosed(false), mInClose(false), mHavePendingClose(false), mHadOriginalOpener(false), mIsPopupSpam(false), mBlockScriptedClosingFlag(false), mFireOfflineStatusChangeEventOnThaw(false), mCreatingInnerWindow(false), mIsChrome(false), mCleanMessageManager(false), mNeedsFocus(true), mHasFocus(false), #if defined(XP_MACOSX) mShowAccelerators(false), mShowFocusRings(false), #else mShowAccelerators(true), mShowFocusRings(true), #endif mShowFocusRingForContent(false), mFocusByKeyOccurred(false), mNotifiedIDDestroyed(false), mTimeoutInsertionPoint(nsnull), mTimeoutPublicIdCounter(1), mTimeoutFiringDepth(0), mJSObject(nsnull), mTimeoutsSuspendDepth(0), mFocusMethod(0), mSerial(0), #ifdef DEBUG mSetOpenerWindowCalled(false), #endif mCleanedUp(false), mCallCleanUpAfterModalDialogCloses(false), mDialogAbuseCount(0), mDialogDisabled(false) { nsLayoutStatics::AddRef(); // Initialize the PRCList (this). PR_INIT_CLIST(this); // Initialize timeout storage PR_INIT_CLIST(&mTimeouts); if (aOuterWindow) { // |this| is an inner window, add this inner window to the outer // window list of inners. PR_INSERT_AFTER(this, aOuterWindow); mObserver = new nsGlobalWindowObserver(this); if (mObserver) { NS_ADDREF(mObserver); nsCOMPtr os = mozilla::services::GetObserverService(); if (os) { // Watch for online/offline status changes so we can fire events. Use // a strong reference. os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false); // Watch for dom-storage2-changed so we can fire storage // events. Use a strong reference. os->AddObserver(mObserver, "dom-storage2-changed", false); } } } else { // |this| is an outer window. Outer windows start out frozen and // remain frozen until they get an inner window, so freeze this // outer window here. Freeze(); mObserver = nsnull; SetIsDOMBinding(); } // We could have failed the first time through trying // to create the entropy collector, so we should // try to get one until we succeed. gRefCnt++; if (gRefCnt == 1) { #if !(defined(NS_DEBUG) || defined(MOZ_ENABLE_JS_DUMP)) Preferences::AddBoolVarCache(&gDOMWindowDumpEnabled, "browser.dom.window.dump.enabled"); #endif Preferences::AddIntVarCache(&gMinTimeoutValue, "dom.min_timeout_value", DEFAULT_MIN_TIMEOUT_VALUE); Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue, "dom.min_background_timeout_value", DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE); } if (gDumpFile == nsnull) { const nsAdoptingCString& fname = Preferences::GetCString("browser.dom.window.dump.file"); if (!fname.IsEmpty()) { // if this fails to open, Dump() knows to just go to stdout // on null. gDumpFile = fopen(fname, "wb+"); } else { gDumpFile = stdout; } } mSerial = ++gSerialCounter; #ifdef DEBUG if (!PR_GetEnv("MOZ_QUIET")) { printf("++DOMWINDOW == %d (%p) [serial = %d] [outer = %p]\n", gRefCnt, static_cast(static_cast(this)), gSerialCounter, static_cast(aOuterWindow)); } #endif #ifdef PR_LOGGING if (gDOMLeakPRLog) PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG, ("DOMWINDOW %p created outer=%p", this, aOuterWindow)); #endif NS_ASSERTION(sWindowsById, "Windows hash table must be created!"); NS_ASSERTION(!sWindowsById->Get(mWindowID), "This window shouldn't be in the hash table yet!"); sWindowsById->Put(mWindowID, this); mEventTargetObjects.Init(); } /* static */ void nsGlobalWindow::Init() { CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector); NS_ASSERTION(gEntropyCollector, "gEntropyCollector should have been initialized!"); #ifdef PR_LOGGING gDOMLeakPRLog = PR_NewLogModule("DOMLeak"); NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!"); #endif sWindowsById = new WindowByIdTable(); // There are two reasons to have Init() failing: if we were not able to // alloc the memory or if the size we want to init is too high. None of them // should happen. #ifdef DEBUG NS_ASSERTION(sWindowsById->Init(), "Init() should not fail!"); #else sWindowsById->Init(); #endif } static PLDHashOperator DisconnectEventTargetObjects(nsPtrHashKey* aKey, void* aClosure) { nsRefPtr target = aKey->GetKey(); target->DisconnectFromOwner(); return PL_DHASH_NEXT; } nsGlobalWindow::~nsGlobalWindow() { mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nsnull); mEventTargetObjects.Clear(); // We have to check if sWindowsById isn't null because ::Shutdown might have // been called. if (sWindowsById) { NS_ASSERTION(sWindowsById->Get(mWindowID), "This window should be in the hash table"); sWindowsById->Remove(mWindowID); } --gRefCnt; #ifdef DEBUG if (!PR_GetEnv("MOZ_QUIET")) { nsCAutoString url; if (mLastOpenedURI) { mLastOpenedURI->GetSpec(url); } printf("--DOMWINDOW == %d (%p) [serial = %d] [outer = %p] [url = %s]\n", gRefCnt, static_cast(static_cast(this)), mSerial, static_cast(mOuterWindow.get()), url.get()); } #endif #ifdef PR_LOGGING if (gDOMLeakPRLog) PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG, ("DOMWINDOW %p destroyed", this)); #endif if (IsOuterWindow()) { JSObject *proxy = GetWrapperPreserveColor(); if (proxy) { js::SetProxyExtra(proxy, 0, js::PrivateValue(NULL)); } // An outer window is destroyed with inner windows still possibly // alive, iterate through the inner windows and null out their // back pointer to this outer, and pull them out of the list of // inner windows. nsGlobalWindow *w; while ((w = (nsGlobalWindow *)PR_LIST_HEAD(this)) != this) { PR_REMOVE_AND_INIT_LINK(w); } } else { Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, mMutationBits ? 1 : 0); if (mListenerManager) { mListenerManager->Disconnect(); mListenerManager = nsnull; } // An inner window is destroyed, pull it out of the outer window's // list if inner windows. PR_REMOVE_LINK(this); // If our outer window's inner window is this window, null out the // outer window's reference to this window that's being deleted. nsGlobalWindow *outer = GetOuterWindowInternal(); if (outer && outer->mInnerWindow == this) { outer->mInnerWindow = nsnull; } } mDocument = nsnull; // Forces Release mDoc = nsnull; NS_ASSERTION(!mArguments, "mArguments wasn't cleaned up properly!"); CleanUp(true); #ifdef DEBUG nsCycleCollector_DEBUG_wasFreed(static_cast(this)); #endif if (mURLProperty) { mURLProperty->ClearWindowReference(); } nsCOMPtr ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); if (ac) ac->RemoveWindowAsListener(this); nsLayoutStatics::Release(); } void nsGlobalWindow::AddEventTargetObject(nsDOMEventTargetHelper* aObject) { mEventTargetObjects.PutEntry(aObject); } void nsGlobalWindow::RemoveEventTargetObject(nsDOMEventTargetHelper* aObject) { mEventTargetObjects.RemoveEntry(aObject); } // static void nsGlobalWindow::ShutDown() { if (gDumpFile && gDumpFile != stdout) { fclose(gDumpFile); } gDumpFile = nsnull; NS_IF_RELEASE(gEntropyCollector); delete sWindowsById; sWindowsById = nsnull; } // static void nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow) { if (aWindow->mCachedXBLPrototypeHandlers.IsInitialized() && aWindow->mCachedXBLPrototypeHandlers.Count() > 0) { aWindow->mCachedXBLPrototypeHandlers.Clear(); nsISupports* supports; aWindow->QueryInterface(NS_GET_IID(nsCycleCollectionISupports), reinterpret_cast(&supports)); NS_ASSERTION(supports, "Failed to QI to nsCycleCollectionISupports?!"); nsContentUtils::DropJSObjects(supports); } } void nsGlobalWindow::MaybeForgiveSpamCount() { if (IsOuterWindow() && IsPopupSpamWindow()) { SetPopupSpamWindow(false); --gOpenPopupSpamCount; NS_ASSERTION(gOpenPopupSpamCount >= 0, "Unbalanced decrement of gOpenPopupSpamCount"); } } void nsGlobalWindow::CleanUp(bool aIgnoreModalDialog) { if (IsOuterWindow() && !aIgnoreModalDialog) { nsGlobalWindow* inner = GetCurrentInnerWindowInternal(); nsCOMPtr dlg(do_QueryObject(inner)); if (dlg) { // The window we're trying to clean up is the outer window of a // modal dialog. Defer cleanup until the window closes, and let // ShowModalDialog take care of calling CleanUp. mCallCleanUpAfterModalDialogCloses = true; return; } } // Guarantee idempotence. if (mCleanedUp) return; mCleanedUp = true; mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nsnull); mEventTargetObjects.Clear(); if (mObserver) { nsCOMPtr os = mozilla::services::GetObserverService(); if (os) { os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC); os->RemoveObserver(mObserver, "dom-storage2-changed"); } // Drop its reference to this dying window, in case for some bogus reason // the object stays around. mObserver->Forget(); NS_RELEASE(mObserver); } mNavigator = nsnull; mScreen = nsnull; mMenubar = nsnull; mToolbar = nsnull; mLocationbar = nsnull; mPersonalbar = nsnull; mStatusbar = nsnull; mScrollbars = nsnull; mLocation = nsnull; mHistory = nsnull; mFrames = nsnull; mWindowUtils = nsnull; mApplicationCache = nsnull; mIndexedDB = nsnull; mPerformance = nsnull; ClearControllers(); mOpener = nsnull; // Forces Release if (mContext) { #ifdef DEBUG nsCycleCollector_DEBUG_shouldBeFreed(mContext); #endif mContext = nsnull; // Forces Release } mChromeEventHandler = nsnull; // Forces Release mParentTarget = nsnull; nsGlobalWindow *inner = GetCurrentInnerWindowInternal(); if (inner) { inner->CleanUp(aIgnoreModalDialog); } if (mCleanMessageManager) { NS_ABORT_IF_FALSE(mIsChrome, "only chrome should have msg manager cleaned"); nsGlobalChromeWindow *asChrome = static_cast(this); if (asChrome->mMessageManager) { static_cast( asChrome->mMessageManager.get())->Disconnect(); } } mInnerWindowHolder = nsnull; mArguments = nsnull; mArgumentsLast = nsnull; mArgumentsOrigin = nsnull; CleanupCachedXBLHandlers(this); #ifdef DEBUG nsCycleCollector_DEBUG_shouldBeFreed(static_cast(this)); #endif } void nsGlobalWindow::ClearControllers() { if (mControllers) { PRUint32 count; mControllers->GetControllerCount(&count); while (count--) { nsCOMPtr controller; mControllers->GetControllerAt(count, getter_AddRefs(controller)); nsCOMPtr context = do_QueryInterface(controller); if (context) context->SetCommandContext(nsnull); } mControllers = nsnull; } } void nsGlobalWindow::FreeInnerObjects() { NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window"); // Make sure that this is called before we null out the document and // other members that the window destroyed observers could // re-create. NotifyDOMWindowDestroyed(this); // Kill all of the workers for this window. nsIScriptContext *scx = GetContextInternal(); JSContext *cx = scx ? scx->GetNativeContext() : nsnull; mozilla::dom::workers::CancelWorkersForWindow(cx, this); // Close all IndexedDB databases for this window. indexedDB::IndexedDatabaseManager* idbManager = indexedDB::IndexedDatabaseManager::Get(); if (idbManager) { idbManager->AbortCloseDatabasesForWindow(this); } ClearAllTimeouts(); mChromeEventHandler = nsnull; if (mListenerManager) { mListenerManager->Disconnect(); mListenerManager = nsnull; } mLocation = nsnull; mHistory = nsnull; if (mNavigator) { mNavigator->Invalidate(); mNavigator = nsnull; } if (mScreen) { mScreen = nsnull; } if (mDocument) { NS_ASSERTION(mDoc, "Why is mDoc null?"); // Remember the document's principal. mDocumentPrincipal = mDoc->NodePrincipal(); } #ifdef DEBUG if (mDocument) nsCycleCollector_DEBUG_shouldBeFreed(nsCOMPtr(do_QueryInterface(mDocument))); #endif // Remove our reference to the document and the document principal. mDocument = nsnull; mDoc = nsnull; mFocusedNode = nsnull; if (mApplicationCache) { static_cast(mApplicationCache.get())->Disconnect(); mApplicationCache = nsnull; } mIndexedDB = nsnull; NotifyWindowIDDestroyed("inner-window-destroyed"); JSObject* obj = FastGetGlobalJSObject(); if (obj) { if (!cx) { cx = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext(); } JSAutoRequest ar(cx); js::NukeChromeCrossCompartmentWrappersForGlobal(cx, obj, js::DontNukeForGlobalObject); } CleanupCachedXBLHandlers(this); #ifdef DEBUG nsCycleCollector_DEBUG_shouldBeFreed(static_cast(this)); #endif } //***************************************************************************** // nsGlobalWindow::nsISupports //***************************************************************************** #define OUTER_WINDOW_ONLY \ if (IsOuterWindow()) { #define END_OUTER_WINDOW_ONLY \ foundInterface = 0; \ } else NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindow) DOMCI_DATA(Window, nsGlobalWindow) // QueryInterface implementation for nsGlobalWindow NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow) // Make sure this matches the cast in nsGlobalWindow::FromWrapper() NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIDOMWindow) NS_INTERFACE_MAP_ENTRY(nsIDOMJSWindow) if (aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) { foundInterface = static_cast(this); if (!sWarnedAboutWindowInternal) { sWarnedAboutWindowInternal = true; nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Extensions", mDoc, nsContentUtils::eDOM_PROPERTIES, "nsIDOMWindowInternalWarning"); } } else NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow) NS_INTERFACE_MAP_ENTRY(nsIDOMStorageIndexedDB) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsIDOMWindowPerformance) NS_INTERFACE_MAP_ENTRY(nsITouchEventReceiver) NS_INTERFACE_MAP_ENTRY(nsIInlineEventHandlers) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window) OUTER_WINDOW_ONLY NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY END_OUTER_WINDOW_ONLY NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow) static PLDHashOperator MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JSObject* aData, void* aClosure) { xpc_UnmarkGrayObject(aData); return PL_DHASH_NEXT; } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow) if (tmp->IsBlackForCC()) { if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) { tmp->mCachedXBLPrototypeHandlers.EnumerateRead(MarkXBLHandlers, nsnull); } nsEventListenerManager* elm = tmp->GetListenerManager(false); if (elm) { elm->UnmarkGrayJSListeners(); } tmp->UnmarkGrayTimers(); return true; } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow) return tmp->IsBlackForCC(); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindow) return tmp->IsBlackForCC(); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGlobalWindow) if (!cb.WantAllTraces() && tmp->IsBlackForCC()) { return NS_SUCCESS_INTERRUPTED_TRAVERSE; } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mControllers) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArguments) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgumentsLast) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mInnerWindowHolder) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOuterWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOpenerScriptPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mListenerManager, nsEventListenerManager) for (nsTimeout* timeout = tmp->FirstTimeout(); tmp->IsTimeout(timeout); timeout = timeout->Next()) { cb.NoteNativeChild(timeout, &NS_CYCLE_COLLECTION_NAME(nsTimeout)); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLocalStorage) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSessionStorage) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mApplicationCache) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocumentPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDoc) // Traverse stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChromeEventHandler) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParentTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFrameElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFocusedNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPendingStorageEvents) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) nsGlobalWindow::CleanupCachedXBLHandlers(tmp); NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mControllers) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mArguments) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mArgumentsLast) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mInnerWindowHolder) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOuterWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOpenerScriptPrincipal) if (tmp->mListenerManager) { tmp->mListenerManager->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mListenerManager) } NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mLocalStorage) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSessionStorage) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mApplicationCache) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocumentPrincipal) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDoc) // Unlink stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChromeEventHandler) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParentTarget) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFrameElement) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFocusedNode) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPendingStorageEvents) NS_IMPL_CYCLE_COLLECTION_UNLINK_END struct TraceData { TraceData(TraceCallback& aCallback, void* aClosure) : callback(aCallback), closure(aClosure) {} TraceCallback& callback; void* closure; }; static PLDHashOperator TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JSObject* aData, void* aClosure) { TraceData* data = static_cast(aClosure); data->callback(aData, "Cached XBL prototype handler", data->closure); return PL_DHASH_NEXT; } NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow) if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) { TraceData data(aCallback, aClosure); tmp->mCachedXBLPrototypeHandlers.EnumerateRead(TraceXBLHandlers, &data); } NS_IMPL_CYCLE_COLLECTION_TRACE_END bool nsGlobalWindow::IsBlackForCC() { return (mDoc && nsCCUncollectableMarker::InGeneration(mDoc->GetMarkedCCGeneration())) || (nsCCUncollectableMarker::sGeneration && IsBlack()); } void nsGlobalWindow::UnmarkGrayTimers() { for (nsTimeout* timeout = FirstTimeout(); timeout && IsTimeout(timeout); timeout = timeout->Next()) { if (timeout->mScriptHandler) { JSObject* o = timeout->mScriptHandler->GetScriptObject(); xpc_UnmarkGrayObject(o); } } } //***************************************************************************** // nsGlobalWindow::nsIScriptGlobalObject //***************************************************************************** nsresult nsGlobalWindow::EnsureScriptEnvironment() { FORWARD_TO_OUTER(EnsureScriptEnvironment, (), NS_ERROR_NOT_INITIALIZED); if (mJSObject) { return NS_OK; } NS_ASSERTION(!GetCurrentInnerWindowInternal(), "mJSObject is null, but we have an inner window?"); nsCOMPtr scriptRuntime; nsresult rv = NS_GetJSRuntime(getter_AddRefs(scriptRuntime)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr context = scriptRuntime->CreateContext(); NS_ASSERTION(!mContext, "Will overwrite mContext!"); // should probably assert the context is clean??? context->WillInitializeContext(); // We need point the context to the global window before initializing it // so that it can make various decisions properly. context->SetGlobalObject(this); rv = context->InitContext(); NS_ENSURE_SUCCESS(rv, rv); if (IsFrame()) { // This window is a [i]frame, don't bother GC'ing when the // frame's context is destroyed since a GC will happen when the // frameset or host document is destroyed anyway. context->SetGCOnDestruction(false); } mContext = context; return NS_OK; } nsIScriptContext * nsGlobalWindow::GetScriptContext() { FORWARD_TO_OUTER(GetScriptContext, (), nsnull); return mContext; } nsIScriptContext * nsGlobalWindow::GetContext() { FORWARD_TO_OUTER(GetContext, (), nsnull); // check GetContext is indeed identical to GetScriptContext() NS_ASSERTION(mContext == GetScriptContext(), "GetContext confused?"); return mContext; } JSObject * nsGlobalWindow::GetGlobalJSObject() { return FastGetGlobalJSObject(); } bool nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument) { // We reuse the inner window when: // a. We are currently at our original document. // b. At least one of the following conditions are true: // -- We are not currently a content window (i.e., we're currently a chrome // window). // -- The new document is the same as the old document. This means that we're // getting called from document.open(). // -- The new document has the same origin as what we have loaded right now. if (!mDoc || !aNewDocument) { return false; } if (!mDoc->IsInitialDocument()) { return false; } NS_ASSERTION(NS_IsAboutBlank(mDoc->GetDocumentURI()), "How'd this happen?"); // Great, we're the original document, check for one of the other // conditions. if (mDoc == aNewDocument) { return true; } bool equal; if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(), &equal)) && equal) { // The origin is the same. return true; } nsCOMPtr treeItem(do_QueryInterface(mDocShell)); if (treeItem) { PRInt32 itemType = nsIDocShellTreeItem::typeContent; treeItem->GetItemType(&itemType); // If we're a chrome window, then we want to reuse the inner window. return itemType == nsIDocShellTreeItem::typeChrome; } // No treeItem: don't reuse the current inner window. return false; } void nsGlobalWindow::SetOpenerScriptPrincipal(nsIPrincipal* aPrincipal) { FORWARD_TO_OUTER_VOID(SetOpenerScriptPrincipal, (aPrincipal)); if (mDoc) { if (!mDoc->IsInitialDocument()) { // We have a document already, and it's not the original one. Bail out. // Do NOT set mOpenerScriptPrincipal in this case, just to be safe. return; } #ifdef DEBUG // We better have an about:blank document loaded at this point. Otherwise, // something is really weird. nsCOMPtr uri; mDoc->NodePrincipal()->GetURI(getter_AddRefs(uri)); NS_ASSERTION(uri && NS_IsAboutBlank(uri) && NS_IsAboutBlank(mDoc->GetDocumentURI()), "Unexpected original document"); #endif GetDocShell()->CreateAboutBlankContentViewer(aPrincipal); mDoc->SetIsInitialDocument(true); nsCOMPtr shell; GetDocShell()->GetPresShell(getter_AddRefs(shell)); if (shell && !shell->DidInitialReflow()) { // Ensure that if someone plays with this document they will get // layout happening. nsRect r = shell->GetPresContext()->GetVisibleArea(); shell->InitialReflow(r.width, r.height); } } } nsIPrincipal* nsGlobalWindow::GetOpenerScriptPrincipal() { FORWARD_TO_OUTER(GetOpenerScriptPrincipal, (), nsnull); return mOpenerScriptPrincipal; } PopupControlState PushPopupControlState(PopupControlState aState, bool aForce) { PopupControlState oldState = gPopupControlState; if (aState < gPopupControlState || aForce) { gPopupControlState = aState; } return oldState; } void PopPopupControlState(PopupControlState aState) { gPopupControlState = aState; } PopupControlState nsGlobalWindow::PushPopupControlState(PopupControlState aState, bool aForce) const { return ::PushPopupControlState(aState, aForce); } void nsGlobalWindow::PopPopupControlState(PopupControlState aState) const { ::PopPopupControlState(aState); } PopupControlState nsGlobalWindow::GetPopupControlState() const { return gPopupControlState; } #define WINDOWSTATEHOLDER_IID \ {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}} class WindowStateHolder : public nsISupports { public: NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID) NS_DECL_ISUPPORTS WindowStateHolder(nsGlobalWindow *aWindow, nsIXPConnectJSObjectHolder *aHolder, nsIXPConnectJSObjectHolder *aOuterProto, nsIXPConnectJSObjectHolder *aOuterRealProto); nsGlobalWindow* GetInnerWindow() { return mInnerWindow; } nsIXPConnectJSObjectHolder *GetInnerWindowHolder() { return mInnerWindowHolder; } nsIXPConnectJSObjectHolder* GetOuterProto() { return mOuterProto; } nsIXPConnectJSObjectHolder* GetOuterRealProto() { return mOuterRealProto; } void DidRestoreWindow() { mInnerWindow = nsnull; mInnerWindowHolder = nsnull; mOuterProto = nsnull; mOuterRealProto = nsnull; } protected: ~WindowStateHolder(); nsGlobalWindow *mInnerWindow; // We hold onto this to make sure the inner window doesn't go away. The outer // window ends up recalculating it anyway. nsCOMPtr mInnerWindowHolder; nsCOMPtr mOuterProto; nsCOMPtr mOuterRealProto; }; NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID) WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow, nsIXPConnectJSObjectHolder *aHolder, nsIXPConnectJSObjectHolder *aOuterProto, nsIXPConnectJSObjectHolder *aOuterRealProto) : mInnerWindow(aWindow), mOuterProto(aOuterProto), mOuterRealProto(aOuterRealProto) { NS_PRECONDITION(aWindow, "null window"); NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window"); mInnerWindowHolder = aHolder; aWindow->SuspendTimeouts(); } WindowStateHolder::~WindowStateHolder() { if (mInnerWindow) { // This window was left in the bfcache and is now going away. We need to // free it up. // Note that FreeInnerObjects may already have been called on the // inner window if its outer has already had SetDocShell(null) // called. mInnerWindow->FreeInnerObjects(); } } NS_IMPL_ISUPPORTS1(WindowStateHolder, WindowStateHolder) nsresult nsGlobalWindow::CreateOuterObject(nsGlobalWindow* aNewInner) { JSContext* cx = mContext->GetNativeContext(); if (IsChromeWindow()) { // Always enable E4X for XUL and other chrome content -- there is no // need to preserve the