/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=2 et tw=78: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "base/basictypes.h" #include /* 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 "nsError.h" #include "nsIIdleService.h" #include "nsIPowerManagerService.h" #ifdef XP_WIN #ifdef GetClassName #undef GetClassName #endif // GetClassName #endif // XP_WIN // Helper Classes #include "nsXPIDLString.h" #include "nsJSUtils.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" #include "mozilla/Likely.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" #include "nsJSPrincipals.h" #include "mozilla/Attributes.h" // Interfaces Needed #include "nsIFrame.h" #include "nsCanvasFrame.h" #include "nsIWidget.h" #include "nsIWidgetListener.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 "nsIEmbeddingSiteWindow.h" #include "nsThreadUtils.h" #include "nsEventStateManager.h" #include "nsIHttpProtocolHandler.h" #include "nsIJSContextStack.h" #include "nsIJSRuntimeService.h" #include "nsILoadContext.h" #include "nsIMarkupDocumentViewer.h" #include "nsIPresShell.h" #include "nsIProgrammingLanguage.h" #include "nsIServiceManager.h" #include "nsIScriptGlobalObjectOwner.h" #include "nsIScriptSecurityManager.h" #include "nsIScrollableFrame.h" #include "nsView.h" #include "nsViewManager.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 "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 "nsIDOMFile.h" #include "nsIDOMFileList.h" #include "nsIURIFixup.h" #include "nsIAppStartup.h" #include "nsToolkitCompsCID.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 "nsXBLService.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 "mozilla/Selection.h" #include "nsFrameLoader.h" #include "nsISupportsPrimitives.h" #include "nsXPCOMCID.h" #include "GeneratedEvents.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" #include "nsIAppsService.h" #include "prrng.h" #include "nsSandboxFlags.h" #include "TimeChangeObserver.h" #include "nsPISocketTransportService.h" #include "mozilla/dom/AudioContext.h" // Apple system headers seem to have a check() macro. #ifdef check #undef check #endif // check #include "AccessCheck.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 namespace mozilla::dom::ipc; using mozilla::TimeStamp; using mozilla::TimeDuration; nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr; bool nsGlobalWindow::sWarnedAboutWindowInternal = false; bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false; static nsIEntropyCollector *gEntropyCollector = nullptr; static int32_t gRefCnt = 0; static int32_t gOpenPopupSpamCount = 0; static PopupControlState gPopupControlState = openAbused; static int32_t gRunningTimeoutDepth = 0; static bool gMouseDown = false; static bool gDragServiceDisabled = false; static FILE *gDumpFile = nullptr; static uint64_t gNextWindowID = 0; static uint32_t gSerialCounter = 0; static uint32_t gTimeoutsRecentlySet = 0; static TimeStamp gLastRecordedRecentTimeouts; #define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC) #ifdef DEBUG_jst int32_t gTimeoutCnt = 0; #endif #if !(defined(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 int32_t gMinTimeoutValue; static int32_t gMinBackgroundTimeoutValue; inline int32_t nsGlobalWindow::DOMMinTimeoutValue() const { bool isBackground = !mOuterWindow || mOuterWindow->IsBackground(); return std::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"; #define NETWORK_UPLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkupload") #define NETWORK_DOWNLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkdownload") /** * An indirect observer object that means we don't have to implement nsIObserver * on nsGlobalWindow, where any script could see it. */ class nsGlobalWindowObserver MOZ_FINAL : public nsIObserver, public nsIInterfaceRequestor { 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 = nullptr; } NS_IMETHODIMP GetInterface(const nsIID& aIID, void** aResult) { if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) { return mWindow->QueryInterface(aIID, aResult); } return NS_NOINTERFACE; } private: nsGlobalWindow* mWindow; }; NS_IMPL_ISUPPORTS2(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor) nsTimeout::nsTimeout() : mCleared(false), mRunning(false), mIsInterval(false), mPublicId(0), mInterval(0), mFiringDepth(0), mNestingLevel(0), mPopupState(openAllowed) { #ifdef DEBUG_jst { extern int gTimeoutCnt; ++gTimeoutCnt; } #endif MOZ_COUNT_CTOR(nsTimeout); } nsTimeout::~nsTimeout() { #ifdef DEBUG_jst { extern int gTimeoutCnt; --gTimeoutCnt; } #endif MOZ_COUNT_DTOR(nsTimeout); } NS_IMPL_CYCLE_COLLECTION_LEGACY_NATIVE_CLASS(nsTimeout) NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsTimeout) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTimeout) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(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(nullptr), mDocShell(nullptr), mModalStateDepth(0), mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false), mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr), mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false), mMayHaveMouseEnterLeaveEventListener(false), mIsModalContentWindow(false), mIsActive(false), mIsBackground(false), mInnerWindow(nullptr), 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) { setSafeToUnwrap(false); } virtual bool isOuterWindow() { return true; } virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE; virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp) MOZ_OVERRIDE; static nsOuterWindowProxy singleton; }; JSString * nsOuterWindowProxy::obj_toString(JSContext *cx, JSObject *proxy) { MOZ_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(); } } bool nsOuterWindowProxy::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id, js::Value *vp) { if (id == nsDOMClassInfo::sWrappedJSObject_id && xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) { *vp = JS::ObjectValue(*wrapper); return true; } return js::Wrapper::get(cx, wrapper, receiver, id, vp); } nsOuterWindowProxy nsOuterWindowProxy::singleton; class nsChromeOuterWindowProxy : public nsOuterWindowProxy { public: nsChromeOuterWindowProxy() : nsOuterWindowProxy() {} virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper); static nsChromeOuterWindowProxy singleton; }; JSString * nsChromeOuterWindowProxy::obj_toString(JSContext *cx, JSObject *proxy) { MOZ_ASSERT(js::IsProxy(proxy)); return JS_NewStringCopyZ(cx, "[object ChromeWindow]"); } nsChromeOuterWindowProxy nsChromeOuterWindowProxy::singleton; static JSObject* NewOuterWindowProxy(JSContext *cx, JSObject *parent, bool isChrome) { JSAutoCompartment ac(cx, parent); JSObject *proto; if (!js::GetObjectProto(cx, parent, &proto)) return nullptr; JSObject *obj = js::Wrapper::New(cx, parent, proto, parent, isChrome ? &nsChromeOuterWindowProxy::singleton : &nsOuterWindowProxy::singleton); NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class"); return obj; } //***************************************************************************** //*** nsGlobalWindow: Object Management //***************************************************************************** nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) : nsPIDOMWindow(aOuterWindow), mIdleFuzzFactor(0), mIdleCallbackIndex(-1), mCurrentlyIdle(false), mAddActiveEventFuzzTime(true), mIsFrozen(false), mFullScreen(false), mIsClosed(false), mInClose(false), mHavePendingClose(false), mHadOriginalOpener(false), mIsPopupSpam(false), mBlockScriptedClosingFlag(false), mFireOfflineStatusChangeEventOnThaw(false), mNotifyIdleObserversIdleOnThaw(false), mNotifyIdleObserversActiveOnThaw(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), mAllowScriptsToClose(false), mTimeoutInsertionPoint(nullptr), mTimeoutPublicIdCounter(1), mTimeoutFiringDepth(0), mJSObject(nullptr), mTimeoutsSuspendDepth(0), mFocusMethod(0), mSerial(0), #ifdef DEBUG mSetOpenerWindowCalled(false), #endif mCleanedUp(false), mCallCleanUpAfterModalDialogCloses(false), mDialogAbuseCount(0), mStopAbuseDialogs(false), mDialogsPermanentlyDisabled(false) { nsLayoutStatics::AddRef(); // Initialize the PRCList (this). PR_INIT_CLIST(this); 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 = nullptr; 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(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); Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled, "dom.idle-observers-api.fuzz_time.disabled", false); } if (gDumpFile == nullptr) { 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(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!"); // We seem to see crashes in release builds because of null |sWindowsById|. if (sWindowsById) { 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(); sWindowsById->Init(); } static PLDHashOperator DisconnectEventTargetObjects(nsPtrHashKey* aKey, void* aClosure) { nsRefPtr target = aKey->GetKey(); target->DisconnectFromOwner(); return PL_DHASH_NEXT; } nsGlobalWindow::~nsGlobalWindow() { mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr); 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")) { nsAutoCString url; if (mLastOpenedURI) { mLastOpenedURI->GetSpec(url); // Data URLs can be very long, so truncate to avoid flooding the log. const uint32_t maxURLLength = 1000; if (url.Length() > maxURLLength) { url.Truncate(maxURLLength); } } nsGlobalWindow* outer = static_cast(mOuterWindow.get()); printf("--DOMWINDOW == %d (%p) [serial = %d] [outer = %p] [url = %s]\n", gRefCnt, static_cast(static_cast(this)), mSerial, static_cast(static_cast(outer)), 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 = nullptr; } // 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->MaybeClearInnerWindow(this); } } mDocument = nullptr; // Forces Release mDoc = nullptr; NS_ASSERTION(!mArguments, "mArguments wasn't cleaned up properly!"); CleanUp(true); #ifdef DEBUG nsCycleCollector_DEBUG_wasFreed(static_cast(this)); #endif 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 = nullptr; NS_IF_RELEASE(gEntropyCollector); delete sWindowsById; sWindowsById = nullptr; } // 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, nullptr); 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"); } #ifdef MOZ_B2G DisableNetworkEvent(NS_NETWORK_UPLOAD_EVENT); DisableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT); #endif // MOZ_B2G if (mIdleService) { mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S); } // Drop its reference to this dying window, in case for some bogus reason // the object stays around. mObserver->Forget(); NS_RELEASE(mObserver); } mNavigator = nullptr; mScreen = nullptr; mMenubar = nullptr; mToolbar = nullptr; mLocationbar = nullptr; mPersonalbar = nullptr; mStatusbar = nullptr; mScrollbars = nullptr; mLocation = nullptr; mHistory = nullptr; mFrames = nullptr; mWindowUtils = nullptr; mApplicationCache = nullptr; mIndexedDB = nullptr; mPerformance = nullptr; ClearControllers(); mOpener = nullptr; // Forces Release if (mContext) { #ifdef DEBUG nsCycleCollector_DEBUG_shouldBeFreed(mContext); #endif mContext = nullptr; // Forces Release } mChromeEventHandler = nullptr; // Forces Release mParentTarget = nullptr; 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 = nullptr; mArguments = nullptr; mArgumentsLast = nullptr; mArgumentsOrigin = nullptr; CleanupCachedXBLHandlers(this); if (mIdleTimer) { mIdleTimer->Cancel(); mIdleTimer = nullptr; } DisableTimeChangeNotifications(); #ifdef DEBUG nsCycleCollector_DEBUG_shouldBeFreed(static_cast(this)); #endif } void nsGlobalWindow::ClearControllers() { if (mControllers) { uint32_t count; mControllers->GetControllerCount(&count); while (count--) { nsCOMPtr controller; mControllers->GetControllerAt(count, getter_AddRefs(controller)); nsCOMPtr context = do_QueryInterface(controller); if (context) context->SetCommandContext(nullptr); } mControllers = nullptr; } } 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() : nullptr; 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(); if (mIdleTimer) { mIdleTimer->Cancel(); mIdleTimer = nullptr; } mIdleObservers.Clear(); mChromeEventHandler = nullptr; if (mListenerManager) { mListenerManager->Disconnect(); mListenerManager = nullptr; } mLocation = nullptr; mHistory = nullptr; if (mNavigator) { mNavigator->OnNavigation(); mNavigator->Invalidate(); mNavigator = nullptr; } if (mScreen) { mScreen->Reset(); mScreen = nullptr; } if (mDocument) { NS_ASSERTION(mDoc, "Why is mDoc null?"); // Remember the document's principal and URI. mDocumentPrincipal = mDoc->NodePrincipal(); mDocumentURI = mDoc->GetDocumentURI(); mDocBaseURI = mDoc->GetDocBaseURI(); } #ifdef DEBUG if (mDocument) nsCycleCollector_DEBUG_shouldBeFreed(nsCOMPtr(do_QueryInterface(mDocument))); #endif // Remove our reference to the document and the document principal. mDocument = nullptr; mDoc = nullptr; mFocusedNode = nullptr; if (mApplicationCache) { static_cast(mApplicationCache.get())->Disconnect(); mApplicationCache = nullptr; } mIndexedDB = nullptr; NotifyWindowIDDestroyed("inner-window-destroyed"); CleanupCachedXBLHandlers(this); for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { mAudioContexts[i]->Shutdown(); } mAudioContexts.Clear(); #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) #ifdef MOZ_B2G NS_INTERFACE_MAP_ENTRY(nsIDOMWindowB2G) #endif // MOZ_B2G 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_INTERFACE_MAP_ENTRY(nsIWindowCrypto) 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, nullptr); } nsEventListenerManager* elm = tmp->GetListenerManager(false); if (elm) { elm->MarkForCC(); } 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 inline void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, IdleObserverHolder& aField, const char* aName, unsigned aFlags) { CycleCollectionNoteChild(aCallback, aField.mIdleObserver.get(), aName, aFlags); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow) if (MOZ_UNLIKELY(cb.WantDebugInfo())) { char name[512]; PR_snprintf(name, sizeof(name), "nsGlobalWindow #%ld", tmp->mWindowID); cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); } else { NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get()) } if (!cb.WantAllTraces() && tmp->IsBlackForCC()) { return NS_SUCCESS_INTERRUPTED_TRAVERSE; } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArgumentsLast) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInnerWindowHolder) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) for (nsTimeout* timeout = tmp->mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) { cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout)); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers) // Traverse stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioContexts) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) nsGlobalWindow::CleanupCachedXBLHandlers(tmp); NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext) NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments) NS_IMPL_CYCLE_COLLECTION_UNLINK(mArgumentsLast) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) NS_IMPL_CYCLE_COLLECTION_UNLINK(mInnerWindowHolder) if (tmp->mOuterWindow) { static_cast(tmp->mOuterWindow.get())->MaybeClearInnerWindow(tmp); NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow) } if (tmp->mListenerManager) { tmp->mListenerManager->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager) } NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage) NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStorageEvents) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers) // Unlink stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler) NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedNode) NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioContexts) 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 = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) { 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); // If 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. nsCOMPtr context = scriptRuntime->CreateContext(!IsFrame(), this); NS_ASSERTION(!mContext, "Will overwrite mContext!"); // should probably assert the context is clean??? context->WillInitializeContext(); rv = context->InitContext(); NS_ENSURE_SUCCESS(rv, rv); mContext = context; return NS_OK; } nsIScriptContext * nsGlobalWindow::GetScriptContext() { FORWARD_TO_OUTER(GetScriptContext, (), nullptr); return mContext; } nsIScriptContext * nsGlobalWindow::GetContext() { FORWARD_TO_OUTER(GetContext, (), nullptr); // 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: // -- 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; } return false; } void nsGlobalWindow::SetInitialPrincipalToSubject() { FORWARD_TO_OUTER_VOID(SetInitialPrincipalToSubject, ()); // First, grab the subject principal. These methods never fail. nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); nsCOMPtr newWindowPrincipal, systemPrincipal; ssm->GetSubjectPrincipal(getter_AddRefs(newWindowPrincipal)); ssm->GetSystemPrincipal(getter_AddRefs(systemPrincipal)); if (!newWindowPrincipal) { newWindowPrincipal = systemPrincipal; } // Now, if we're about to use the system principal, make sure we're not using // it for a content docshell. if (newWindowPrincipal == systemPrincipal) { int32_t itemType; nsCOMPtr item = do_QueryInterface(GetDocShell()); nsresult rv = item->GetItemType(&itemType); if (NS_FAILED(rv) || itemType != nsIDocShellTreeItem::typeChrome) { newWindowPrincipal = nullptr; } } // If there's an existing document, bail if it either: if (mDoc) { // (a) is not an initial about:blank document, or if (!mDoc->IsInitialDocument()) return; // (b) already has the correct principal. if (mDoc->NodePrincipal() == newWindowPrincipal) return; #ifdef DEBUG // If we have a document loaded at this point, it had better be about:blank. // 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(newWindowPrincipal); mDoc->SetIsInitialDocument(true); nsCOMPtr shell = GetDocShell()->GetPresShell(); if (shell && !shell->DidInitialize()) { // Ensure that if someone plays with this document they will get // layout happening. nsRect r = shell->GetPresContext()->GetVisibleArea(); shell->Initialize(r.width, r.height); } } 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 MOZ_FINAL : public nsISupports { public: NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID) NS_DECL_ISUPPORTS WindowStateHolder(nsGlobalWindow *aWindow, nsIXPConnectJSObjectHolder *aHolder); nsGlobalWindow* GetInnerWindow() { return mInnerWindow; } nsIXPConnectJSObjectHolder *GetInnerWindowHolder() { return mInnerWindowHolder; } void DidRestoreWindow() { mInnerWindow = nullptr; mInnerWindowHolder = nullptr; } 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; }; NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID) WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow, nsIXPConnectJSObjectHolder *aHolder) : mInnerWindow(aWindow) { 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(); bool isChrome = IsChromeWindow(); if (isChrome) { // Always enable E4X for XUL and other chrome content -- there is no // need to preserve the