/* -*- 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" // 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 "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 "nsDeviceMotion.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 "nsIPrefBranch.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"; class nsDummyJavaPluginOwner : public nsIPluginInstanceOwner { public: nsDummyJavaPluginOwner(nsIDocument *aDocument) : mDocument(aDocument) { } void Destroy(); NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIPLUGININSTANCEOWNER NS_IMETHOD GetURL(const char *aURL, const char *aTarget, nsIInputStream *aPostStream, void *aHeadersData, PRUint32 aHeadersDataLen); NS_IMETHOD ShowStatus(const PRUnichar *aStatusMsg); NPError ShowNativeContextMenu(NPMenu* menu, void* event); NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace); void SendIdleEvent(); NPError InitAsyncSurface(NPSize *size, NPImageFormat format, void *initData, NPAsyncSurface *surface) { return NPERR_GENERIC_ERROR; } NPError FinalizeAsyncSurface(NPAsyncSurface *surface) { return NPERR_GENERIC_ERROR; } void SetCurrentAsyncSurface(NPAsyncSurface *surface, NPRect *changed) { return; } NS_DECL_CYCLE_COLLECTION_CLASS(nsDummyJavaPluginOwner) private: nsRefPtr mInstance; nsCOMPtr mDocument; }; NS_IMPL_CYCLE_COLLECTION_2(nsDummyJavaPluginOwner, mDocument, mInstance) // QueryInterface implementation for nsDummyJavaPluginOwner NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDummyJavaPluginOwner) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY(nsIPluginInstanceOwner) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDummyJavaPluginOwner) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDummyJavaPluginOwner) void nsDummyJavaPluginOwner::Destroy() { // If we have a plugin instance, stop it and destroy it now. if (mInstance) { mInstance->Stop(); mInstance->InvalidateOwner(); mInstance = nsnull; } mDocument = nsnull; } NS_IMETHODIMP nsDummyJavaPluginOwner::SetInstance(nsNPAPIPluginInstance *aInstance) { // If we're going to null out mInstance after use, be sure to call // mInstance->InvalidateOwner() here, since it now won't be called // from nsDummyJavaPluginOwner::Destroy(). if (mInstance && !aInstance) mInstance->InvalidateOwner(); mInstance = aInstance; return NS_OK; } NS_IMETHODIMP nsDummyJavaPluginOwner::GetInstance(nsNPAPIPluginInstance **aInstance) { *aInstance = mInstance; NS_IF_ADDREF(*aInstance); return NS_OK; } NS_IMETHODIMP nsDummyJavaPluginOwner::GetWindow(NPWindow *&aWindow) { aWindow = nsnull; return NS_OK; } NS_IMETHODIMP nsDummyJavaPluginOwner::CallSetWindow() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDummyJavaPluginOwner::GetMode(PRInt32 *aMode) { // This is wrong, but there's no better alternative. *aMode = NP_EMBED; return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDummyJavaPluginOwner::CreateWidget(void) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDummyJavaPluginOwner::GetURL(const char *aURL, const char *aTarget, nsIInputStream *aPostStream, void *aHeadersData, PRUint32 aHeadersDataLen) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDummyJavaPluginOwner::ShowStatus(const char *aStatusMsg) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDummyJavaPluginOwner::ShowStatus(const PRUnichar *aStatusMsg) { return NS_ERROR_NOT_IMPLEMENTED; } NPError nsDummyJavaPluginOwner::ShowNativeContextMenu(NPMenu* menu, void* event) { return NPERR_GENERIC_ERROR; } NPBool nsDummyJavaPluginOwner::ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace) { return false; } NS_IMETHODIMP nsDummyJavaPluginOwner::GetDocument(nsIDocument **aDocument) { NS_IF_ADDREF(*aDocument = mDocument); return NS_OK; } NS_IMETHODIMP nsDummyJavaPluginOwner::InvalidateRect(NPRect *invalidRect) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDummyJavaPluginOwner::InvalidateRegion(NPRegion invalidRegion) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDummyJavaPluginOwner::RedrawPlugin() { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDummyJavaPluginOwner::GetNetscapeWindow(void *value) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsDummyJavaPluginOwner::SetEventModel(PRInt32 eventModel) { return NS_ERROR_NOT_IMPLEMENTED; } void nsDummyJavaPluginOwner::SendIdleEvent() { } /** * 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 //***************************************************************************** JSString * nsOuterWindowProxy::obj_toString(JSContext *cx, JSObject *proxy) { JS_ASSERT(js::IsProxy(proxy)); return JS_NewStringCopyZ(cx, "[object Window]"); } void nsOuterWindowProxy::finalize(JSContext *cx, JSObject *proxy) { nsISupports *global = static_cast(js::GetProxyExtra(proxy, 0).toPrivate()); if (global) { nsWrapperCache *cache; CallQueryInterface(global, &cache); cache->ClearWrapperIfProxy(); } } nsOuterWindowProxy nsOuterWindowProxy::singleton; JSObject * NS_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), mDidInitJavaProperties(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), mHasDeviceMotion(false), mNotifiedIDDestroyed(false), mTimeoutInsertionPoint(nsnull), mTimeoutPublicIdCounter(1), mTimeoutFiringDepth(0), mJSObject(nsnull), mPendingStorageEventsObsolete(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-storage-changed so we can fire storage // events. Use a strong reference. os->AddObserver(mObserver, "dom-storage2-changed", false); os->AddObserver(mObserver, "dom-storage-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; SetIsProxy(); } // 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(); } DisableDeviceMotionUpdates(); mHasDeviceMotion = false; 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"); os->RemoveObserver(mObserver, "dom-storage-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; mApplicationCache = nsnull; mIndexedDB = nsnull; mPendingStorageEventsObsolete = 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 (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"); if (mDummyJavaPluginOwner) { // Tear down the dummy java plugin. // XXXjst: On a general note, should windows with java stuff in // them ever even make it into the fast-back cache? mDummyJavaPluginOwner->Destroy(); mDummyJavaPluginOwner = nsnull; mDidInitJavaProperties = false; } 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(const void* 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); } 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(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) // Traverse mDummyJavaPluginOwner NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDummyJavaPluginOwner) 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(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) // Unlink mDummyJavaPluginOwner if (tmp->mDummyJavaPluginOwner) { tmp->mDummyJavaPluginOwner->Destroy(); NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDummyJavaPluginOwner) tmp->mDidInitJavaProperties = false; } 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(const void* aKey, JSObject* aData, void* aClosure) { TraceData* data = static_cast(aClosure); data->callback(nsIProgrammingLanguage::JAVASCRIPT, 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::SetScriptContext(PRUint32 lang_id, nsIScriptContext *aScriptContext) { NS_ASSERTION(lang_id == nsIProgrammingLanguage::JAVASCRIPT, "We don't support this language ID"); NS_ASSERTION(IsOuterWindow(), "Uh, SetScriptContext() called on inner window!"); NS_ASSERTION(!aScriptContext || !mContext, "Bad call to SetContext()!"); if (aScriptContext) { // should probably assert the context is clean??? aScriptContext->WillInitializeContext(); nsresult rv = aScriptContext->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. aScriptContext->SetGCOnDestruction(false); } } mContext = aScriptContext; return NS_OK; } nsresult nsGlobalWindow::EnsureScriptEnvironment(PRUint32 aLangID) { NS_ASSERTION(aLangID == nsIProgrammingLanguage::JAVASCRIPT, "We don't support this language ID"); FORWARD_TO_OUTER(EnsureScriptEnvironment, (aLangID), 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_GetScriptRuntimeByID(aLangID, getter_AddRefs(scriptRuntime)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr context = scriptRuntime->CreateContext(); return SetScriptContext(aLangID, context); } nsIScriptContext * nsGlobalWindow::GetScriptContext(PRUint32 lang) { NS_ASSERTION(lang == nsIProgrammingLanguage::JAVASCRIPT, "We don't support this language ID"); FORWARD_TO_OUTER(GetScriptContext, (lang), nsnull); return mContext; } nsIScriptContext * nsGlobalWindow::GetContext() { FORWARD_TO_OUTER(GetContext, (), nsnull); // check GetContext is indeed identical to GetScriptContext() NS_ASSERTION(mContext == GetScriptContext(nsIProgrammingLanguage::JAVASCRIPT), "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) struct ReparentWaiverClosure { JSContext *mCx; JSObject *mNewInner; }; static JSDHashOperator ReparentWaiverWrappers(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { ReparentWaiverClosure *closure = static_cast(arg); JSObject *value = static_cast(hdr)->value; // We reparent wrappers that have as their parent an inner window whose // outer has the new inner window as its current inner. JSObject *parent = JS_GetParent(value); JSObject *outer = JS_ObjectToOuterObject(closure->mCx, parent); if (outer) { JSObject *inner = JS_ObjectToInnerObject(closure->mCx, outer); if (inner == closure->mNewInner && inner != parent) JS_SetParent(closure->mCx, value, closure->mNewInner); } else { JS_ClearPendingException(closure->mCx); } return JS_DHASH_NEXT; } nsresult nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, nsISupports* aState, bool aForceReuseInnerWindow) { NS_TIME_FUNCTION; NS_PRECONDITION(mDocumentPrincipal == nsnull, "mDocumentPrincipal prematurely set!"); if (!aDocument) { NS_ERROR("SetNewDocument(null) called!"); return NS_ERROR_INVALID_ARG; } if (IsInnerWindow()) { if (!mOuterWindow) { return NS_ERROR_NOT_INITIALIZED; } // Refuse to set a new document if the call came from an inner // window that's not the current inner window. if (mOuterWindow->GetCurrentInnerWindow() != this) { return NS_ERROR_NOT_AVAILABLE; } return GetOuterWindowInternal()->SetNewDocument(aDocument, aState, aForceReuseInnerWindow); } NS_PRECONDITION(IsOuterWindow(), "Must only be called on outer windows"); if (IsFrozen()) { // This outer is now getting its first inner, thaw the outer now // that it's ready and is getting an inner window. Thaw(); } NS_ASSERTION(!GetCurrentInnerWindow() || GetCurrentInnerWindow()->GetExtantDocument() == mDocument, "Uh, mDocument doesn't match the current inner window " "document!"); bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument); if (aForceReuseInnerWindow && !wouldReuseInnerWindow && mDoc && mDoc->NodePrincipal() != aDocument->NodePrincipal()) { NS_ERROR("Attempted forced inner window reuse while changing principal"); return NS_ERROR_UNEXPECTED; } nsCOMPtr oldDoc(do_QueryInterface(mDocument)); nsIScriptContext *scx = GetContextInternal(); NS_ENSURE_TRUE(scx, NS_ERROR_NOT_INITIALIZED); JSContext *cx = scx->GetNativeContext(); #ifndef MOZ_DISABLE_DOMCRYPTO // clear smartcard events, our document has gone away. if (mCrypto) { mCrypto->SetEnableSmartCardEvents(false); } #endif if (!mDocument) { // First document load. // Get our private root. If it is equal to us, then we need to // attach our global key bindings that handles browser scrolling // and other browser commands. nsIDOMWindow* privateRoot = nsGlobalWindow::GetPrivateRoot(); if (privateRoot == static_cast(this)) { nsCOMPtr xblService = do_GetService("@mozilla.org/xbl;1"); if (xblService) { xblService->AttachGlobalKeyHandler(mChromeEventHandler); } } } /* No mDocShell means we're already been partially closed down. When that happens, setting status isn't a big requirement, so don't. (Doesn't happen under normal circumstances, but bug 49615 describes a case.) */ nsContentUtils::AddScriptRunner( NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus)); bool reUseInnerWindow = aForceReuseInnerWindow || wouldReuseInnerWindow; nsresult rv = NS_OK; // Set mDocument even if this is an outer window to avoid // having to *always* reach into the inner window to find the // document. mDocument = do_QueryInterface(aDocument); mDoc = aDocument; #ifdef DEBUG mLastOpenedURI = aDocument->GetDocumentURI(); #endif mContext->WillInitializeContext(); nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); nsRefPtr newInnerWindow; bool createdInnerWindow = false; bool thisChrome = IsChromeWindow(); bool isChrome = false; nsCxPusher cxPusher; if (!cxPusher.Push(cx)) { return NS_ERROR_FAILURE; } JSAutoRequest ar(cx); nsCOMPtr wsh = do_QueryInterface(aState); NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?"); if (reUseInnerWindow) { // We're reusing the current inner window. NS_ASSERTION(!currentInner->IsFrozen(), "We should never be reusing a shared inner window"); newInnerWindow = currentInner; if (aDocument != oldDoc) { nsWindowSH::InvalidateGlobalScopePolluter(cx, currentInner->mJSObject); } // The API we're really looking for here is to go clear all of the // Xray wrappers associated with our outer window. However, we // don't expose that API because the implementation would be // identical to that of JS_TransplantObject, so we just call that // instead. if (!JS_TransplantObject(cx, mJSObject, mJSObject)) { return NS_ERROR_FAILURE; } } else { if (aState) { newInnerWindow = wsh->GetInnerWindow(); mInnerWindowHolder = wsh->GetInnerWindowHolder(); NS_ASSERTION(newInnerWindow, "Got a state without inner window"); } else if (thisChrome) { newInnerWindow = new nsGlobalChromeWindow(this); isChrome = true; } else if (mIsModalContentWindow) { newInnerWindow = new nsGlobalModalWindow(this); } else { newInnerWindow = new nsGlobalWindow(this); } if (!aState) { // This is redundant if we're restoring from a previous inner window. nsIScriptGlobalObject *sgo = (nsIScriptGlobalObject *)newInnerWindow.get(); // Freeze the outer window and null out the inner window so // that initializing classes on the new inner doesn't end up // reaching into the old inner window for classes etc. // // [This happens with Object.prototype when XPConnect creates // a temporary global while initializing classes; the reason // being that xpconnect creates the temp global w/o a parent // and proto, which makes the JS engine look up classes in // cx->globalObject, i.e. this outer window]. mInnerWindow = nsnull; Freeze(); mCreatingInnerWindow = true; // Every script context we are initialized with must create a // new global. nsCOMPtr &holder = mInnerWindowHolder; rv = mContext->CreateNativeGlobalForInner(sgo, isChrome, aDocument->NodePrincipal(), &newInnerWindow->mJSObject, getter_AddRefs(holder)); NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerWindow->mJSObject && holder, "Failed to get script global and holder"); mCreatingInnerWindow = false; createdInnerWindow = true; Thaw(); NS_ENSURE_SUCCESS(rv, rv); } if (currentInner && currentInner->mJSObject) { if (oldDoc == aDocument) { // Move the navigator from the old inner window to the new one since // this is a document.write. This is safe from a same-origin point of // view because document.write can only be used by the same origin. newInnerWindow->mNavigator = currentInner->mNavigator; currentInner->mNavigator = nsnull; if (newInnerWindow->mNavigator) { newInnerWindow->mNavigator->SetWindow(newInnerWindow); } } // Don't free objects on our current inner window if it's going to be // held in the bfcache. if (!currentInner->IsFrozen()) { currentInner->FreeInnerObjects(); } } mInnerWindow = newInnerWindow; if (!mJSObject) { mContext->CreateOuterObject(this, newInnerWindow); mContext->DidInitializeContext(); mJSObject = mContext->GetNativeGlobal(); SetWrapper(mJSObject); } else { JSObject *outerObject = NS_NewOuterWindowProxy(cx, newInnerWindow->mJSObject); if (!outerObject) { NS_ERROR("out of memory"); return NS_ERROR_FAILURE; } js::SetProxyExtra(mJSObject, 0, js::PrivateValue(NULL)); outerObject = JS_TransplantObject(cx, mJSObject, outerObject); if (!outerObject) { NS_ERROR("unable to transplant wrappers, probably OOM"); return NS_ERROR_FAILURE; } nsIScriptGlobalObject *global = static_cast(this); js::SetProxyExtra(outerObject, 0, js::PrivateValue(global)); mJSObject = outerObject; SetWrapper(mJSObject); { JSAutoEnterCompartment ac; if (!ac.enter(cx, mJSObject)) { NS_ERROR("unable to enter a compartment"); return NS_ERROR_FAILURE; } JS_SetParent(cx, mJSObject, newInnerWindow->mJSObject); mContext->SetOuterObject(mJSObject); JSCompartment *compartment = js::GetObjectCompartment(mJSObject); xpc::CompartmentPrivate *priv = static_cast(JS_GetCompartmentPrivate(compartment)); if (priv && priv->waiverWrapperMap) { NS_ASSERTION(!JS_IsExceptionPending(cx), "We might overwrite a pending exception!"); ReparentWaiverClosure closure = { cx, newInnerWindow->mJSObject }; priv->waiverWrapperMap->Enumerate(ReparentWaiverWrappers, &closure); } } } // If we created a new inner window above, we need to do the last little bit // of initialization now that the dust has settled. if (createdInnerWindow) { nsIXPConnect *xpc = nsContentUtils::XPConnect(); nsCOMPtr wrapper; nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerWindow->mJSObject, getter_AddRefs(wrapper)); NS_ENSURE_SUCCESS(rv, rv); NS_ABORT_IF_FALSE(wrapper, "bad wrapper"); rv = wrapper->FinishInitForWrappedGlobal(); NS_ENSURE_SUCCESS(rv, rv); } JSAutoEnterCompartment ac; if (!ac.enter(cx, mJSObject)) { NS_ERROR("unable to enter a compartment"); return NS_ERROR_FAILURE; } // XXX Not sure if this is needed. if (aState) { JSObject *proto; if (nsIXPConnectJSObjectHolder *holder = wsh->GetOuterRealProto()) { holder->GetJSObject(&proto); } else { proto = nsnull; } if (!JS_SetPrototype(cx, mJSObject, proto)) { NS_ERROR("can't set prototype"); return NS_ERROR_FAILURE; } } else { if (!JS_DefineProperty(cx, newInnerWindow->mJSObject, "window", OBJECT_TO_JSVAL(mJSObject), JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { NS_ERROR("can't create the 'window' property"); return NS_ERROR_FAILURE; } } } JSAutoEnterCompartment ac; if (!ac.enter(cx, mJSObject)) { NS_ERROR("unable to enter a compartment"); return NS_ERROR_FAILURE; } if (!aState && !reUseInnerWindow) { // Loading a new page and creating a new inner window, *not* // restoring from session history. // Now that both the the inner and outer windows are initialized // let the script context do its magic to hook them together. mContext->ConnectToInner(newInnerWindow, mJSObject); nsCOMPtr frame = do_QueryInterface(GetFrameElementInternal()); if (frame && frame->OwnerDoc()) { nsPIDOMWindow* parentWindow = frame->OwnerDoc()->GetWindow(); if (parentWindow && parentWindow->TimeoutSuspendCount()) { SuspendTimeouts(parentWindow->TimeoutSuspendCount()); } } } // Add an extra ref in case we release mContext during GC. nsCOMPtr kungFuDeathGrip(mContext); // Now that the prototype is all set up, install the global scope // polluter. This must happen after the above prototype fixup. If // the GSP was to be installed on the inner window's real // prototype (as it would be if this was done before the prototype // fixup above) we would end up holding the GSP alive (through // XPConnect's internal marking of wrapper prototypes) as long as // the inner window was around, and if the GSP had properties on // it that held an element alive we'd hold the document alive, // which could hold event handlers alive, which hold the context // alive etc. if ((!reUseInnerWindow || aDocument != oldDoc) && !aState) { nsCOMPtr html_doc(do_QueryInterface(mDocument)); nsWindowSH::InstallGlobalScopePolluter(cx, newInnerWindow->mJSObject, html_doc); } if (aDocument) { aDocument->SetScriptGlobalObject(newInnerWindow); } if (!aState) { if (reUseInnerWindow) { if (newInnerWindow->mDoc != aDocument) { newInnerWindow->mDocument = do_QueryInterface(aDocument); newInnerWindow->mDoc = aDocument; // We're reusing the inner window for a new document. In this // case we don't clear the inner window's scope, but we must // make sure the cached document property gets updated. // XXXmarkh - tell other languages about this? ::JS_DeleteProperty(cx, currentInner->mJSObject, "document"); if (mDummyJavaPluginOwner) { // Since we're reusing the inner window, tear down the // dummy Java plugin we created for the old document in // this window. mDummyJavaPluginOwner->Destroy(); mDummyJavaPluginOwner = nsnull; mDidInitJavaProperties = false; } } } else { rv = newInnerWindow->InnerSetNewDocument(aDocument); NS_ENSURE_SUCCESS(rv, rv); // Initialize DOM classes etc on the inner window. rv = mContext->InitClasses(newInnerWindow->mJSObject); NS_ENSURE_SUCCESS(rv, rv); } if (mArguments) { newInnerWindow->DefineArgumentsProperty(mArguments); newInnerWindow->mArguments = mArguments; newInnerWindow->mArgumentsOrigin = mArgumentsOrigin; mArguments = nsnull; mArgumentsOrigin = nsnull; } // Give the new inner window our chrome event handler (since it // doesn't have one). newInnerWindow->mChromeEventHandler = mChromeEventHandler; } mContext->GC(js::gcreason::SET_NEW_DOCUMENT); mContext->DidInitializeContext(); if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) { // We should probably notify. However if this is the, arguably bad, // situation when we're creating a temporary non-chrome-about-blank // document in a chrome docshell, don't notify just yet. Instead wait // until we have a real chrome doc. nsCOMPtr treeItem(do_QueryInterface(mDocShell)); PRInt32 itemType = nsIDocShellTreeItem::typeContent; if (treeItem) { treeItem->GetItemType(&itemType); } if (itemType != nsIDocShellTreeItem::typeChrome || nsContentUtils::IsSystemPrincipal(mDoc->NodePrincipal())) { newInnerWindow->mHasNotifiedGlobalCreated = true; nsContentUtils::AddScriptRunner( NS_NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated)); } } return NS_OK; } void nsGlobalWindow::DispatchDOMWindowCreated() { if (!mDoc || !mDocument) { return; } // Fire DOMWindowCreated at chrome event listeners nsContentUtils::DispatchChromeEvent(mDoc, mDocument, NS_LITERAL_STRING("DOMWindowCreated"), true /* bubbles */, false /* not cancellable */); nsCOMPtr observerService = mozilla::services::GetObserverService(); if (observerService) { nsAutoString origin; nsIPrincipal* principal = mDoc->NodePrincipal(); nsContentUtils::GetUTFOrigin(principal, origin); observerService-> NotifyObservers(static_cast(this), nsContentUtils::IsSystemPrincipal(principal) ? "chrome-document-global-created" : "content-document-global-created", origin.get()); } } void nsGlobalWindow::ClearStatus() { SetStatus(EmptyString()); SetDefaultStatus(EmptyString()); } nsresult nsGlobalWindow::InnerSetNewDocument(nsIDocument* aDocument) { NS_PRECONDITION(IsInnerWindow(), "Must only be called on inner windows"); #ifdef PR_LOGGING if (aDocument && gDOMLeakPRLog && PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) { nsIURI *uri = aDocument->GetDocumentURI(); nsCAutoString spec; if (uri) uri->GetSpec(spec); PR_LogPrint("DOMWINDOW %p SetNewDocument %s", this, spec.get()); } #endif mDocument = do_QueryInterface(aDocument); mDoc = aDocument; mFocusedNode = nsnull; mLocalStorage = nsnull; mSessionStorage = nsnull; #ifdef DEBUG mLastOpenedURI = aDocument->GetDocumentURI(); #endif Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, mMutationBits ? 1 : 0); // Clear our mutation bitfield. mMutationBits = 0; return NS_OK; } void nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell) { NS_ASSERTION(IsOuterWindow(), "Uh, SetDocShell() called on inner window!"); if (aDocShell == mDocShell) return; // SetDocShell(nsnull) means the window is being torn down. Drop our // reference to the script context, allowing it to be deleted // later. Meanwhile, keep our weak reference to the script object // (mJSObject) so that it can be retrieved later (until it is // finalized by the JS GC). if (!aDocShell) { NS_ASSERTION(PR_CLIST_IS_EMPTY(&mTimeouts), "Uh, outer window holds timeouts!"); // Call FreeInnerObjects on all inner windows, not just the current // one, since some could be held by WindowStateHolder objects that // are GC-owned. for (nsRefPtr inner = (nsGlobalWindow *)PR_LIST_HEAD(this); inner != this; inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) { NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this, "bad outer window pointer"); inner->FreeInnerObjects(); } // Make sure that this is called before we null out the document. NotifyDOMWindowDestroyed(this); NotifyWindowIDDestroyed("outer-window-destroyed"); nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); if (currentInner) { NS_ASSERTION(mDoc, "Must have doc!"); // Remember the document's principal. mDocumentPrincipal = mDoc->NodePrincipal(); // Release our document reference mDocument = nsnull; mDoc = nsnull; mFocusedNode = nsnull; } ClearControllers(); mChromeEventHandler = nsnull; // force release now if (mArguments) { // We got no new document after someone called // SetArguments(), drop our reference to the arguments. mArguments = nsnull; mArgumentsLast = nsnull; mArgumentsOrigin = nsnull; } if (mContext) { mContext->GC(js::gcreason::SET_DOC_SHELL); mContext = nsnull; } #ifdef DEBUG nsCycleCollector_DEBUG_shouldBeFreed(mContext); nsCycleCollector_DEBUG_shouldBeFreed(static_cast(this)); #endif } mDocShell = aDocShell; // Weak Reference NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!"); if (mFrames) mFrames->SetDocShell(aDocShell); if (mScreen) mScreen->SetDocShell(aDocShell); if (!mDocShell) { MaybeForgiveSpamCount(); CleanUp(false); } else { // Get our enclosing chrome shell and retrieve its global window impl, so // that we can do some forwarding to the chrome document. nsCOMPtr chromeEventHandler; mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler)); mChromeEventHandler = do_QueryInterface(chromeEventHandler); if (!mChromeEventHandler) { // We have no chrome event handler. If we have a parent, // get our chrome event handler from the parent. If // we don't have a parent, then we need to make a new // window root object that will function as a chrome event // handler and receive all events that occur anywhere inside // our window. nsCOMPtr parentWindow; GetParent(getter_AddRefs(parentWindow)); if (parentWindow.get() != static_cast(this)) { nsCOMPtr piWindow(do_QueryInterface(parentWindow)); mChromeEventHandler = piWindow->GetChromeEventHandler(); } else NS_NewWindowRoot(this, getter_AddRefs(mChromeEventHandler)); } bool docShellActive; mDocShell->GetIsActive(&docShellActive); mIsBackground = !docShellActive; } } void nsGlobalWindow::SetOpenerWindow(nsIDOMWindow* aOpener, bool aOriginalOpener) { FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener)); NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled, "aOriginalOpener is true, but not first call to " "SetOpenerWindow!"); NS_ASSERTION(aOpener || !aOriginalOpener, "Shouldn't set mHadOriginalOpener if aOpener is null"); mOpener = do_GetWeakReference(aOpener); NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!"); if (aOriginalOpener) { mHadOriginalOpener = true; } #ifdef DEBUG mSetOpenerWindowCalled = true; #endif } void nsGlobalWindow::UpdateParentTarget() { nsCOMPtr flo = do_QueryInterface(mChromeEventHandler); if (flo) { nsRefPtr fl = flo->GetFrameLoader(); if (fl) { mParentTarget = fl->GetTabChildGlobalAsEventTarget(); } } if (!mParentTarget) { mParentTarget = mChromeEventHandler; } } bool nsGlobalWindow::GetIsTabModalPromptAllowed() { bool allowTabModal = true; if (mDocShell) { nsCOMPtr cv; mDocShell->GetContentViewer(getter_AddRefs(cv)); cv->GetIsTabModalPromptAllowed(&allowTabModal); } return allowTabModal; } nsIDOMEventTarget* nsGlobalWindow::GetTargetForDOMEvent() { return static_cast(GetOuterWindowInternal()); } nsIDOMEventTarget* nsGlobalWindow::GetTargetForEventTargetChain() { return IsInnerWindow() ? this : static_cast(GetCurrentInnerWindowInternal()); } nsresult nsGlobalWindow::WillHandleEvent(nsEventChainPostVisitor& aVisitor) { return NS_OK; } JSContext* nsGlobalWindow::GetJSContextForEventHandlers() { return nsnull; } nsresult nsGlobalWindow::PreHandleEvent(nsEventChainPreVisitor& aVisitor) { NS_PRECONDITION(IsInnerWindow(), "PreHandleEvent is used on outer window!?"); static PRUint32 count = 0; PRUint32 msg = aVisitor.mEvent->message; aVisitor.mCanHandle = true; aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 if ((msg == NS_MOUSE_MOVE) && gEntropyCollector) { //Chances are this counter will overflow during the life of the //process, but that's OK for our case. Means we get a little //more entropy. if (count++ % 100 == 0) { //Since the high bits seem to be zero's most of the time, //let's only take the lowest half of the point structure. PRInt16 myCoord[2]; myCoord[0] = aVisitor.mEvent->refPoint.x; myCoord[1] = aVisitor.mEvent->refPoint.y; gEntropyCollector->RandomUpdate((void*)myCoord, sizeof(myCoord)); gEntropyCollector->RandomUpdate((void*)&(aVisitor.mEvent->time), sizeof(PRUint32)); } } else if (msg == NS_RESIZE_EVENT) { mIsHandlingResizeEvent = true; } else if (msg == NS_MOUSE_BUTTON_DOWN && NS_IS_TRUSTED_EVENT(aVisitor.mEvent)) { gMouseDown = true; } else if ((msg == NS_MOUSE_BUTTON_UP || msg == NS_DRAGDROP_END) && NS_IS_TRUSTED_EVENT(aVisitor.mEvent)) { gMouseDown = false; if (gDragServiceDisabled) { nsCOMPtr ds = do_GetService("@mozilla.org/widget/dragservice;1"); if (ds) { gDragServiceDisabled = false; ds->Unsuppress(); } } } aVisitor.mParentTarget = GetParentTarget(); return NS_OK; } bool nsGlobalWindow::DialogOpenAttempted() { nsGlobalWindow *topWindow = GetTop(); if (!topWindow) { NS_ERROR("DialogOpenAttempted() called without a top window?"); return false; } topWindow = topWindow->GetCurrentInnerWindowInternal(); if (!topWindow || topWindow->mLastDialogQuitTime.IsNull() || nsContentUtils::CallerHasUniversalXPConnect()) { return false; } TimeDuration dialogDuration(TimeStamp::Now() - topWindow->mLastDialogQuitTime); if (dialogDuration.ToSeconds() < Preferences::GetInt("dom.successive_dialog_time_limit", SUCCESSIVE_DIALOG_TIME_LIMIT)) { topWindow->mDialogAbuseCount++; return (topWindow->GetPopupControlState() > openAllowed || topWindow->mDialogAbuseCount > MAX_DIALOG_COUNT); } topWindow->mDialogAbuseCount = 0; return false; } bool nsGlobalWindow::AreDialogsBlocked() { nsGlobalWindow *topWindow = GetTop(); if (!topWindow) { NS_ASSERTION(!mDocShell, "AreDialogsBlocked() called without a top window?"); return true; } topWindow = topWindow->GetCurrentInnerWindowInternal(); return !topWindow || (topWindow->mDialogDisabled && (topWindow->GetPopupControlState() > openAllowed || topWindow->mDialogAbuseCount >= MAX_DIALOG_COUNT)); } bool nsGlobalWindow::ConfirmDialogAllowed() { FORWARD_TO_OUTER(ConfirmDialogAllowed, (), false); NS_ENSURE_TRUE(mDocShell, false); nsCOMPtr promptSvc = do_GetService("@mozilla.org/embedcomp/prompt-service;1"); if (!DialogOpenAttempted() || !promptSvc) { return true; } // Reset popup state while opening a modal dialog, and firing events // about the dialog, to prevent the current state from being active // the whole time a modal dialog is open. nsAutoPopupStatePusher popupStatePusher(openAbused, true); bool disableDialog = false; nsXPIDLString label, title; nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDialogLabel", label); nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, "ScriptDialogPreventTitle", title); promptSvc->Confirm(this, title.get(), label.get(), &disableDialog); if (disableDialog) { PreventFurtherDialogs(); return false; } return true; } void nsGlobalWindow::PreventFurtherDialogs() { nsGlobalWindow *topWindow = GetTop(); if (!topWindow) { NS_ERROR("PreventFurtherDialogs() called without a top window?"); return; } topWindow = topWindow->GetCurrentInnerWindowInternal(); if (topWindow) topWindow->mDialogDisabled = true; } nsresult nsGlobalWindow::PostHandleEvent(nsEventChainPostVisitor& aVisitor) { NS_PRECONDITION(IsInnerWindow(), "PostHandleEvent is used on outer window!?"); // Return early if there is nothing to do. switch (aVisitor.mEvent->message) { case NS_RESIZE_EVENT: case NS_PAGE_UNLOAD: case NS_LOAD: break; default: return NS_OK; } /* mChromeEventHandler and mContext go dangling in the middle of this function under some circumstances (events that destroy the window) without this addref. */ nsCOMPtr kungFuDeathGrip1(mChromeEventHandler); nsCOMPtr kungFuDeathGrip2(GetContextInternal()); if (aVisitor.mEvent->message == NS_RESIZE_EVENT) { mIsHandlingResizeEvent = false; } else if (aVisitor.mEvent->message == NS_PAGE_UNLOAD && NS_IS_TRUSTED_EVENT(aVisitor.mEvent)) { // Execute bindingdetached handlers before we tear ourselves // down. if (mDocument) { NS_ASSERTION(mDoc, "Must have doc"); mDoc->BindingManager()->ExecuteDetachedHandlers(); } mIsDocumentLoaded = false; } else if (aVisitor.mEvent->message == NS_LOAD && NS_IS_TRUSTED_EVENT(aVisitor.mEvent)) { // This is page load event since load events don't propagate to |window|. // @see nsDocument::PreHandleEvent. mIsDocumentLoaded = true; nsCOMPtr content(do_QueryInterface(GetFrameElementInternal())); nsCOMPtr treeItem = do_QueryInterface(GetDocShell()); PRInt32 itemType = nsIDocShellTreeItem::typeChrome; if (treeItem) { treeItem->GetItemType(&itemType); } if (content && GetParentInternal() && itemType != nsIDocShellTreeItem::typeChrome) { // If we're not in chrome, or at a chrome boundary, fire the // onload event for the frame element. nsEventStatus status = nsEventStatus_eIgnore; nsEvent event(NS_IS_TRUSTED_EVENT(aVisitor.mEvent), NS_LOAD); event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; // Most of the time we could get a pres context to pass in here, // but not always (i.e. if this window is not shown there won't // be a pres context available). Since we're not firing a GUI // event we don't need a pres context anyway so we just pass // null as the pres context all the time here. nsEventDispatcher::Dispatch(content, nsnull, &event, nsnull, &status); } } return NS_OK; } nsresult nsGlobalWindow::DispatchDOMEvent(nsEvent* aEvent, nsIDOMEvent* aDOMEvent, nsPresContext* aPresContext, nsEventStatus* aEventStatus) { return nsEventDispatcher::DispatchDOMEvent(static_cast(this), aEvent, aDOMEvent, aPresContext, aEventStatus); } void nsGlobalWindow::OnFinalize(JSObject* aObject) { if (aObject == mJSObject) { mJSObject = NULL; } } void nsGlobalWindow::SetScriptsEnabled(bool aEnabled, bool aFireTimeouts) { FORWARD_TO_INNER_VOID(SetScriptsEnabled, (aEnabled, aFireTimeouts)); if (aEnabled && aFireTimeouts) { // Scripts are enabled (again?) on this context, run timeouts that // fired on this context while scripts were disabled. void (nsGlobalWindow::*run)() = &nsGlobalWindow::RunTimeout; NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, run)); } } nsresult nsGlobalWindow::SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin) { FORWARD_TO_OUTER(SetArguments, (aArguments, aOrigin), NS_ERROR_NOT_INITIALIZED); // Hold on to the arguments so that we can re-set them once the next // document is loaded. mArguments = aArguments; mArgumentsOrigin = aOrigin; nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); if (!mIsModalContentWindow) { mArgumentsLast = aArguments; } else if (currentInner) { // SetArguments() is being called on a modal content window that // already has an inner window. This can happen when loading // javascript: URIs as modal content dialogs. In this case, we'll // set up the dialog window, both inner and outer, before we call // SetArguments() on the window, so to deal with that, make sure // here that the arguments are propagated to the inner window. currentInner->mArguments = aArguments; currentInner->mArgumentsOrigin = aOrigin; } return currentInner ? currentInner->DefineArgumentsProperty(aArguments) : NS_OK; } nsresult nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments) { JSContext *cx; nsIScriptContext *ctx = GetOuterWindowInternal()->mContext; NS_ENSURE_TRUE(aArguments && ctx && (cx = ctx->GetNativeContext()), NS_ERROR_NOT_INITIALIZED); if (mIsModalContentWindow) { // Modal content windows don't have an "arguments" property, they // have a "dialogArguments" property which is handled // separately. See nsWindowSH::NewResolve(). return NS_OK; } return GetContextInternal()->SetProperty(mJSObject, "arguments", aArguments); } //***************************************************************************** // nsGlobalWindow::nsIScriptObjectPrincipal //***************************************************************************** nsIPrincipal* nsGlobalWindow::GetPrincipal() { if (mDoc) { // If we have a document, get the principal from the document return mDoc->NodePrincipal(); } if (mDocumentPrincipal) { return mDocumentPrincipal; } // If we don't have a principal and we don't have a document we // ask the parent window for the principal. This can happen when // loading a frameset that has a , in // that case the global window is used in JS before we've loaded // a document into the window. nsCOMPtr objPrincipal = do_QueryInterface(GetParentInternal()); if (objPrincipal) { return objPrincipal->GetPrincipal(); } return nsnull; } //***************************************************************************** // nsGlobalWindow::nsIDOMWindow //***************************************************************************** NS_IMETHODIMP nsGlobalWindow::GetDocument(nsIDOMDocument** aDocument) { // This method *should* forward calls to the outer window, but since // there's nothing here that *depends* on anything in the outer // (GetDocShell() eliminates that dependency), we won't do that to // avoid the extra virtual function call. // lazily instantiate an about:blank document if necessary, and if // we have what it takes to do so. Note that domdoc here is the same // thing as our mDocument, but we don't have to explicitly set the // member variable because the docshell has already called // SetNewDocument(). nsIDocShell *docShell; if (!mDocument && (docShell = GetDocShell())) nsCOMPtr domdoc(do_GetInterface(docShell)); NS_IF_ADDREF(*aDocument = mDocument); return NS_OK; } NS_IMETHODIMP nsGlobalWindow::GetWindow(nsIDOMWindow** aWindow) { FORWARD_TO_OUTER(GetWindow, (aWindow), NS_ERROR_NOT_INITIALIZED); *aWindow = static_cast(this); NS_ADDREF(*aWindow); return NS_OK; } NS_IMETHODIMP nsGlobalWindow::GetSelf(nsIDOMWindow** aWindow) { FORWARD_TO_OUTER(GetSelf, (aWindow), NS_ERROR_NOT_INITIALIZED); *aWindow = static_cast(this); NS_ADDREF(*aWindow); return NS_OK; } NS_IMETHODIMP nsGlobalWindow::GetNavigator(nsIDOMNavigator** aNavigator) { FORWARD_TO_INNER(GetNavigator, (aNavigator), NS_ERROR_NOT_INITIALIZED); *aNavigator = nsnull; if (!mNavigator) { mNavigator = new Navigator(this); } NS_ADDREF(*aNavigator = mNavigator); return NS_OK; } NS_IMETHODIMP nsGlobalWindow::GetScreen(nsIDOMScreen** aScreen) { FORWARD_TO_OUTER(GetScreen, (aScreen), NS_ERROR_NOT_INITIALIZED); *aScreen = nsnull; if (!mScreen && mDocShell) { mScreen = new nsScreen(mDocShell); if (!mScreen) { return NS_ERROR_OUT_OF_MEMORY; } } NS_IF_ADDREF(*aScreen = mScreen); return NS_OK; } NS_IMETHODIMP nsGlobalWindow::GetHistory(nsIDOMHistory** aHistory) { FORWARD_TO_INNER(GetHistory, (aHistory), NS_ERROR_NOT_INITIALIZED); *aHistory = nsnull; if (!mHistory) { mHistory = new nsHistory(this); if (!mHistory) { return NS_ERROR_OUT_OF_MEMORY; } } NS_IF_ADDREF(*aHistory = mHistory); return NS_OK; } NS_IMETHODIMP nsGlobalWindow::GetPerformance(nsIDOMPerformance** aPerformance) { FORWARD_TO_INNER(GetPerformance, (aPerformance), NS_ERROR_NOT_INITIALIZED); *aPerformance = nsnull; if (nsGlobalWindow::HasPerformanceSupport()) { if (!mPerformance) { if (!mDoc) { return NS_OK; } nsRefPtr timing = mDoc->GetNavigationTiming(); nsCOMPtr timedChannel(do_QueryInterface(mDoc->GetChannel())); bool timingEnabled = false; if (!timedChannel || !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) || !timingEnabled) { timedChannel = nsnull; } if (timing) { mPerformance = new nsPerformance(timing, timedChannel); } } NS_IF_ADDREF(*aPerformance = mPerformance); } return NS_OK; } /** * GetScriptableParent is called when script reads window.parent. * * In contrast to GetRealParent, GetScriptableParent respects