michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsGlobalWindow.h" michael@0: michael@0: #include michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: // Local Includes michael@0: #include "Navigator.h" michael@0: #include "nsScreen.h" michael@0: #include "nsHistory.h" michael@0: #include "nsPerformance.h" michael@0: #include "nsDOMNavigationTiming.h" michael@0: #include "nsIDOMStorage.h" michael@0: #include "nsIDOMStorageManager.h" michael@0: #include "DOMStorage.h" michael@0: #include "nsDOMOfflineResourceList.h" michael@0: #include "nsError.h" michael@0: #include "nsIIdleService.h" michael@0: #include "nsISizeOfEventTarget.h" michael@0: #include "nsDOMJSUtils.h" michael@0: #include "nsArrayUtils.h" michael@0: #include "nsIDOMWindowCollection.h" michael@0: #include "nsDOMWindowList.h" michael@0: #include "mozilla/dom/WakeLock.h" michael@0: #include "mozilla/dom/power/PowerManagerService.h" michael@0: #include "nsIDocShellTreeOwner.h" michael@0: #include "nsIPermissionManager.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsIScriptTimeoutHandler.h" michael@0: #include "nsIController.h" michael@0: #include "nsScriptNameSpaceManager.h" michael@0: #include "nsWindowMemoryReporter.h" michael@0: michael@0: // Helper Classes michael@0: #include "nsJSUtils.h" michael@0: #include "jsapi.h" // for JSAutoRequest michael@0: #include "js/OldDebugAPI.h" // for JS_ClearWatchPointsForObject michael@0: #include "jswrapper.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsDOMClassInfo.h" michael@0: #include "nsJSEnvironment.h" michael@0: #include "ScriptSettings.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "mozilla/unused.h" michael@0: michael@0: // Other Classes michael@0: #include "mozilla/dom/BarProps.h" michael@0: #include "nsContentCID.h" michael@0: #include "nsLayoutStatics.h" michael@0: #include "nsCCUncollectableMarker.h" michael@0: #include "mozilla/dom/workers/Workers.h" michael@0: #include "mozilla/dom/MessagePortList.h" michael@0: #include "nsJSPrincipals.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/Debug.h" michael@0: #include "mozilla/EventListenerManager.h" michael@0: #include "mozilla/EventStates.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "AudioChannelService.h" michael@0: #include "MessageEvent.h" michael@0: michael@0: // Interfaces Needed michael@0: #include "nsIFrame.h" michael@0: #include "nsCanvasFrame.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsIWidgetListener.h" michael@0: #include "nsIBaseWindow.h" michael@0: #include "nsIDeviceSensors.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIDocCharset.h" michael@0: #include "nsIDocument.h" michael@0: #include "Crypto.h" michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: #include "nsIDOMCryptoLegacy.h" michael@0: #endif michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMEvent.h" michael@0: #include "nsIDOMPopupBlockedEvent.h" michael@0: #include "nsIDOMPopStateEvent.h" michael@0: #include "nsIDOMHashChangeEvent.h" michael@0: #include "nsIDOMOfflineResourceList.h" michael@0: #include "nsPIDOMStorage.h" michael@0: #include "nsDOMString.h" michael@0: #include "nsIEmbeddingSiteWindow.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsILoadContext.h" michael@0: #include "nsIMarkupDocumentViewer.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsView.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsISelectionController.h" michael@0: #include "nsISelection.h" michael@0: #include "nsIPrompt.h" michael@0: #include "nsIPromptService.h" michael@0: #include "nsIPromptFactory.h" michael@0: #include "nsIWritablePropertyBag2.h" michael@0: #include "nsIWebNavigation.h" michael@0: #include "nsIWebBrowserChrome.h" michael@0: #include "nsIWebBrowserFind.h" // For window.find() michael@0: #include "nsIWindowMediator.h" // For window.find() michael@0: #include "nsComputedDOMStyle.h" michael@0: #include "nsIEntropyCollector.h" michael@0: #include "nsDOMCID.h" michael@0: #include "nsDOMWindowUtils.h" michael@0: #include "nsIWindowWatcher.h" michael@0: #include "nsPIWindowWatcher.h" michael@0: #include "nsIContentViewer.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsIControllers.h" michael@0: #include "nsIControllerContext.h" michael@0: #include "nsGlobalWindowCommands.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsCSSProps.h" michael@0: #include "nsIDOMFile.h" michael@0: #include "nsIDOMFileList.h" michael@0: #include "nsIURIFixup.h" michael@0: #ifndef DEBUG michael@0: #include "nsIAppStartup.h" michael@0: #include "nsToolkitCompsCID.h" michael@0: #endif michael@0: #include "nsCDefaultURIFixup.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsFocusManager.h" michael@0: #include "nsIXULWindow.h" michael@0: #include "nsITimedChannel.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #ifdef MOZ_XUL michael@0: #include "nsIDOMXULControlElement.h" michael@0: #include "nsMenuPopupFrame.h" michael@0: #endif michael@0: #include "nsIDOMCustomEvent.h" michael@0: #include "nsIFrameRequestCallback.h" michael@0: #include "nsIJARChannel.h" michael@0: michael@0: #include "xpcprivate.h" michael@0: michael@0: #ifdef NS_PRINTING michael@0: #include "nsIPrintSettings.h" michael@0: #include "nsIPrintSettingsService.h" michael@0: #include "nsIWebBrowserPrint.h" michael@0: #endif michael@0: michael@0: #include "nsWindowRoot.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsIArray.h" michael@0: michael@0: // XXX An unfortunate dependency exists here (two XUL files). michael@0: #include "nsIDOMXULDocument.h" michael@0: #include "nsIDOMXULCommandDispatcher.h" michael@0: michael@0: #include "nsBindingManager.h" michael@0: #include "nsXBLService.h" michael@0: michael@0: // used for popup blocking, needs to be converted to something michael@0: // belonging to the back-end like nsIContentPolicy michael@0: #include "nsIPopupWindowManager.h" michael@0: michael@0: #include "nsIDragService.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/Selection.h" michael@0: #include "nsFrameLoader.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsXPCOMCID.h" michael@0: #include "GeneratedEvents.h" michael@0: #include "GeneratedEventClasses.h" michael@0: #include "mozIThirdPartyUtil.h" michael@0: #ifdef MOZ_LOGGING michael@0: // so we can get logging even in release builds michael@0: #define FORCE_PR_LOG 1 michael@0: #endif michael@0: #include "prlog.h" michael@0: #include "prenv.h" michael@0: #include "prprf.h" michael@0: michael@0: #include "mozilla/dom/MessageChannel.h" michael@0: #include "mozilla/dom/MessagePort.h" michael@0: #include "mozilla/dom/MessagePortBinding.h" michael@0: #include "mozilla/dom/indexedDB/IDBFactory.h" michael@0: #include "mozilla/dom/quota/QuotaManager.h" michael@0: michael@0: #include "mozilla/dom/StructuredCloneTags.h" michael@0: michael@0: #ifdef MOZ_GAMEPAD michael@0: #include "mozilla/dom/GamepadService.h" michael@0: #endif michael@0: michael@0: #include "nsRefreshDriver.h" michael@0: michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/Telemetry.h" michael@0: #include "nsLocation.h" michael@0: #include "nsHTMLDocument.h" michael@0: #include "nsWrapperCacheInlines.h" michael@0: #include "mozilla/DOMEventTargetHelper.h" michael@0: #include "prrng.h" michael@0: #include "nsSandboxFlags.h" michael@0: #include "TimeChangeObserver.h" michael@0: #include "mozilla/dom/AudioContext.h" michael@0: #include "mozilla/dom/BrowserElementDictionariesBinding.h" michael@0: #include "mozilla/dom/Console.h" michael@0: #include "mozilla/dom/FunctionBinding.h" michael@0: #include "mozilla/dom/WindowBinding.h" michael@0: #include "nsITabChild.h" michael@0: #include "mozilla/dom/MediaQueryList.h" michael@0: #include "mozilla/dom/ScriptSettings.h" michael@0: #ifdef HAVE_SIDEBAR michael@0: #include "mozilla/dom/ExternalBinding.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_WEBSPEECH michael@0: #include "mozilla/dom/SpeechSynthesis.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_JSDEBUGGER michael@0: #include "jsdIDebuggerService.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_B2G michael@0: #include "nsPISocketTransportService.h" michael@0: #endif michael@0: michael@0: // Apple system headers seem to have a check() macro. michael@0: #ifdef check michael@0: class nsIScriptTimeoutHandler; michael@0: #undef check michael@0: #endif // check michael@0: #include "AccessCheck.h" michael@0: michael@0: #ifdef ANDROID michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef PR_LOGGING michael@0: static PRLogModuleInfo* gDOMLeakPRLog; michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #define getpid _getpid michael@0: #else michael@0: #include // for getpid() michael@0: #endif michael@0: michael@0: static const char kStorageEnabled[] = "dom.storage.enabled"; michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::dom::ipc; michael@0: using mozilla::TimeStamp; michael@0: using mozilla::TimeDuration; michael@0: michael@0: nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr; michael@0: bool nsGlobalWindow::sWarnedAboutWindowInternal = false; michael@0: bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false; michael@0: michael@0: static nsIEntropyCollector *gEntropyCollector = nullptr; michael@0: static int32_t gRefCnt = 0; michael@0: static int32_t gOpenPopupSpamCount = 0; michael@0: static PopupControlState gPopupControlState = openAbused; michael@0: static int32_t gRunningTimeoutDepth = 0; michael@0: static bool gMouseDown = false; michael@0: static bool gDragServiceDisabled = false; michael@0: static FILE *gDumpFile = nullptr; michael@0: static uint64_t gNextWindowID = 0; michael@0: static uint32_t gSerialCounter = 0; michael@0: static uint32_t gTimeoutsRecentlySet = 0; michael@0: static TimeStamp gLastRecordedRecentTimeouts; michael@0: #define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC) michael@0: michael@0: #ifdef DEBUG_jst michael@0: int32_t gTimeoutCnt = 0; michael@0: #endif michael@0: michael@0: #if defined(DEBUG_bryner) || defined(DEBUG_chb) michael@0: #define DEBUG_PAGE_CACHE michael@0: #endif michael@0: michael@0: #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added" michael@0: michael@0: // The default shortest interval/timeout we permit michael@0: #define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms michael@0: #define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms michael@0: static int32_t gMinTimeoutValue; michael@0: static int32_t gMinBackgroundTimeoutValue; michael@0: inline int32_t michael@0: nsGlobalWindow::DOMMinTimeoutValue() const { michael@0: bool isBackground = !mOuterWindow || mOuterWindow->IsBackground(); michael@0: return michael@0: std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, 0); michael@0: } michael@0: michael@0: // The number of nested timeouts before we start clamping. HTML5 says 1, WebKit michael@0: // uses 5. michael@0: #define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5 michael@0: michael@0: // The longest interval (as PRIntervalTime) we permit, or that our michael@0: // timer code can handle, really. See DELAY_INTERVAL_LIMIT in michael@0: // nsTimerImpl.h for details. michael@0: #define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT michael@0: michael@0: #define FORWARD_TO_OUTER(method, args, err_rval) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsInnerWindow()) { \ michael@0: nsGlobalWindow *outer = GetOuterWindowInternal(); \ michael@0: if (!HasActiveDocument()) { \ michael@0: NS_WARNING(outer ? \ michael@0: "Inner window does not have active document." : \ michael@0: "No outer window available!"); \ michael@0: return err_rval; \ michael@0: } \ michael@0: return outer->method args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsInnerWindow()) { \ michael@0: nsGlobalWindow *outer = GetOuterWindowInternal(); \ michael@0: if (!HasActiveDocument()) { \ michael@0: if (!outer) { \ michael@0: NS_WARNING("No outer window available!"); \ michael@0: errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \ michael@0: } else { \ michael@0: errorresult.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \ michael@0: } \ michael@0: } else { \ michael@0: return outer->method args; \ michael@0: } \ michael@0: return err_rval; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define FORWARD_TO_OUTER_VOID(method, args) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsInnerWindow()) { \ michael@0: nsGlobalWindow *outer = GetOuterWindowInternal(); \ michael@0: if (!HasActiveDocument()) { \ michael@0: NS_WARNING(outer ? \ michael@0: "Inner window does not have active document." : \ michael@0: "No outer window available!"); \ michael@0: return; \ michael@0: } \ michael@0: outer->method args; \ michael@0: return; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define FORWARD_TO_OUTER_CHROME(method, args, err_rval) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsInnerWindow()) { \ michael@0: nsGlobalWindow *outer = GetOuterWindowInternal(); \ michael@0: if (!HasActiveDocument()) { \ michael@0: NS_WARNING(outer ? \ michael@0: "Inner window does not have active document." : \ michael@0: "No outer window available!"); \ michael@0: return err_rval; \ michael@0: } \ michael@0: return ((nsGlobalChromeWindow *)outer)->method args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define FORWARD_TO_INNER_CHROME(method, args, err_rval) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsOuterWindow()) { \ michael@0: if (!mInnerWindow) { \ michael@0: NS_WARNING("No inner window available!"); \ michael@0: return err_rval; \ michael@0: } \ michael@0: return ((nsGlobalChromeWindow *)mInnerWindow)->method args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsInnerWindow()) { \ michael@0: nsGlobalWindow *outer = GetOuterWindowInternal(); \ michael@0: if (!HasActiveDocument()) { \ michael@0: NS_WARNING(outer ? \ michael@0: "Inner window does not have active document." : \ michael@0: "No outer window available!"); \ michael@0: return err_rval; \ michael@0: } \ michael@0: return ((nsGlobalModalWindow *)outer)->method args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define FORWARD_TO_INNER(method, args, err_rval) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsOuterWindow()) { \ michael@0: if (!mInnerWindow) { \ michael@0: NS_WARNING("No inner window available!"); \ michael@0: return err_rval; \ michael@0: } \ michael@0: return GetCurrentInnerWindowInternal()->method args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define FORWARD_TO_INNER_OR_THROW(method, args, errorresult, err_rval) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsOuterWindow()) { \ michael@0: if (!mInnerWindow) { \ michael@0: NS_WARNING("No inner window available!"); \ michael@0: errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \ michael@0: return err_rval; \ michael@0: } \ michael@0: return GetCurrentInnerWindowInternal()->method args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define FORWARD_TO_INNER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsOuterWindow()) { \ michael@0: if (!mInnerWindow) { \ michael@0: NS_WARNING("No inner window available!"); \ michael@0: return err_rval; \ michael@0: } \ michael@0: return ((nsGlobalModalWindow*)GetCurrentInnerWindowInternal())->method args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define FORWARD_TO_INNER_VOID(method, args) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsOuterWindow()) { \ michael@0: if (!mInnerWindow) { \ michael@0: NS_WARNING("No inner window available!"); \ michael@0: return; \ michael@0: } \ michael@0: GetCurrentInnerWindowInternal()->method args; \ michael@0: return; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: // Same as FORWARD_TO_INNER, but this will create a fresh inner if an michael@0: // inner doesn't already exists. michael@0: #define FORWARD_TO_INNER_CREATE(method, args, err_rval) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (IsOuterWindow()) { \ michael@0: if (!mInnerWindow) { \ michael@0: if (mIsClosed) { \ michael@0: return err_rval; \ michael@0: } \ michael@0: nsCOMPtr doc; \ michael@0: nsresult fwdic_nr = GetDocument(getter_AddRefs(doc)); \ michael@0: NS_ENSURE_SUCCESS(fwdic_nr, err_rval); \ michael@0: if (!mInnerWindow) { \ michael@0: return err_rval; \ michael@0: } \ michael@0: } \ michael@0: return GetCurrentInnerWindowInternal()->method args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: // CIDs michael@0: static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID); michael@0: michael@0: static const char sPopStatePrefStr[] = "browser.history.allowPopState"; michael@0: michael@0: #define NETWORK_UPLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkupload") michael@0: #define NETWORK_DOWNLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkdownload") michael@0: michael@0: /** michael@0: * An indirect observer object that means we don't have to implement nsIObserver michael@0: * on nsGlobalWindow, where any script could see it. michael@0: */ michael@0: class nsGlobalWindowObserver MOZ_FINAL : public nsIObserver, michael@0: public nsIInterfaceRequestor michael@0: { michael@0: public: michael@0: nsGlobalWindowObserver(nsGlobalWindow* aWindow) : mWindow(aWindow) {} michael@0: NS_DECL_ISUPPORTS michael@0: NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) michael@0: { michael@0: if (!mWindow) michael@0: return NS_OK; michael@0: return mWindow->Observe(aSubject, aTopic, aData); michael@0: } michael@0: void Forget() { mWindow = nullptr; } michael@0: NS_IMETHODIMP GetInterface(const nsIID& aIID, void** aResult) michael@0: { michael@0: if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) { michael@0: return mWindow->QueryInterface(aIID, aResult); michael@0: } michael@0: return NS_NOINTERFACE; michael@0: } michael@0: michael@0: private: michael@0: nsGlobalWindow* mWindow; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor) michael@0: michael@0: nsTimeout::nsTimeout() michael@0: : mCleared(false), michael@0: mRunning(false), michael@0: mIsInterval(false), michael@0: mPublicId(0), michael@0: mInterval(0), michael@0: mFiringDepth(0), michael@0: mNestingLevel(0), michael@0: mPopupState(openAllowed) michael@0: { michael@0: #ifdef DEBUG_jst michael@0: { michael@0: extern int gTimeoutCnt; michael@0: michael@0: ++gTimeoutCnt; michael@0: } michael@0: #endif michael@0: michael@0: MOZ_COUNT_CTOR(nsTimeout); michael@0: } michael@0: michael@0: nsTimeout::~nsTimeout() michael@0: { michael@0: #ifdef DEBUG_jst michael@0: { michael@0: extern int gTimeoutCnt; michael@0: michael@0: --gTimeoutCnt; michael@0: } michael@0: #endif michael@0: michael@0: if (mTimer) { michael@0: mTimer->Cancel(); michael@0: mTimer = nullptr; michael@0: } michael@0: michael@0: MOZ_COUNT_DTOR(nsTimeout); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsTimeout) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsTimeout) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTimeout) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptHandler) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTimeout, AddRef) michael@0: NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTimeout, Release) michael@0: michael@0: // Return true if this timeout has a refcount of 1. This is used to check michael@0: // that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout. michael@0: bool michael@0: nsTimeout::HasRefCntOne() michael@0: { michael@0: return mRefCnt.get() == 1; michael@0: } michael@0: michael@0: nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow) michael@0: : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0), michael@0: mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false), michael@0: mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr), michael@0: mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false), michael@0: mMayHaveMouseEnterLeaveEventListener(false), michael@0: mMayHavePointerEnterLeaveEventListener(false), michael@0: mIsModalContentWindow(false), michael@0: mIsActive(false), mIsBackground(false), michael@0: mAudioMuted(false), mAudioVolume(1.0), michael@0: mInnerWindow(nullptr), mOuterWindow(aOuterWindow), michael@0: // Make sure no actual window ends up with mWindowID == 0 michael@0: mWindowID(++gNextWindowID), mHasNotifiedGlobalCreated(false), michael@0: mMarkedCCGeneration(0) michael@0: {} michael@0: michael@0: nsPIDOMWindow::~nsPIDOMWindow() {} michael@0: michael@0: // DialogValueHolder CC goop. michael@0: NS_IMPL_CYCLE_COLLECTION(DialogValueHolder, mValue) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DialogValueHolder) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(DialogValueHolder) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogValueHolder) michael@0: michael@0: //***************************************************************************** michael@0: // nsOuterWindowProxy: Outer Window Proxy michael@0: //***************************************************************************** michael@0: michael@0: class nsOuterWindowProxy : public js::Wrapper michael@0: { michael@0: public: michael@0: nsOuterWindowProxy() : js::Wrapper(0) { } michael@0: michael@0: virtual bool finalizeInBackground(JS::Value priv) { michael@0: return false; michael@0: } michael@0: michael@0: virtual const char *className(JSContext *cx, michael@0: JS::Handle wrapper) MOZ_OVERRIDE; michael@0: virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE; michael@0: michael@0: // Fundamental traps michael@0: virtual bool isExtensible(JSContext *cx, JS::Handle proxy, bool *extensible) michael@0: MOZ_OVERRIDE; michael@0: virtual bool preventExtensions(JSContext *cx, michael@0: JS::Handle proxy) MOZ_OVERRIDE; michael@0: virtual bool getPropertyDescriptor(JSContext* cx, michael@0: JS::Handle proxy, michael@0: JS::Handle id, michael@0: JS::MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool getOwnPropertyDescriptor(JSContext* cx, michael@0: JS::Handle proxy, michael@0: JS::Handle id, michael@0: JS::MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool defineProperty(JSContext* cx, michael@0: JS::Handle proxy, michael@0: JS::Handle id, michael@0: JS::MutableHandle desc) MOZ_OVERRIDE; michael@0: virtual bool getOwnPropertyNames(JSContext *cx, michael@0: JS::Handle proxy, michael@0: JS::AutoIdVector &props) MOZ_OVERRIDE; michael@0: virtual bool delete_(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id, michael@0: bool *bp) MOZ_OVERRIDE; michael@0: virtual bool enumerate(JSContext *cx, JS::Handle proxy, michael@0: JS::AutoIdVector &props) MOZ_OVERRIDE; michael@0: michael@0: virtual bool watch(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id, JS::Handle callable) MOZ_OVERRIDE; michael@0: virtual bool unwatch(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id) MOZ_OVERRIDE; michael@0: michael@0: // Derived traps michael@0: virtual bool has(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id, bool *bp) MOZ_OVERRIDE; michael@0: virtual bool hasOwn(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id, bool *bp) MOZ_OVERRIDE; michael@0: virtual bool get(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle receiver, michael@0: JS::Handle id, michael@0: JS::MutableHandle vp) MOZ_OVERRIDE; michael@0: virtual bool set(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle receiver, michael@0: JS::Handle id, michael@0: bool strict, michael@0: JS::MutableHandle vp) MOZ_OVERRIDE; michael@0: virtual bool keys(JSContext *cx, JS::Handle proxy, michael@0: JS::AutoIdVector &props) MOZ_OVERRIDE; michael@0: virtual bool iterate(JSContext *cx, JS::Handle proxy, michael@0: unsigned flags, michael@0: JS::MutableHandle vp) MOZ_OVERRIDE; michael@0: michael@0: static nsOuterWindowProxy singleton; michael@0: michael@0: protected: michael@0: nsGlobalWindow* GetWindow(JSObject *proxy) michael@0: { michael@0: return nsGlobalWindow::FromSupports( michael@0: static_cast(js::GetProxyExtra(proxy, 0).toPrivate())); michael@0: } michael@0: michael@0: // False return value means we threw an exception. True return value michael@0: // but false "found" means we didn't have a subframe at that index. michael@0: bool GetSubframeWindow(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id, michael@0: JS::MutableHandle vp, michael@0: bool &found); michael@0: michael@0: // Returns a non-null window only if id is an index and we have a michael@0: // window at that index. michael@0: already_AddRefed GetSubframeWindow(JSContext *cx, michael@0: JS::Handle proxy, michael@0: JS::Handle id); michael@0: michael@0: bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy, michael@0: JS::AutoIdVector &props); michael@0: }; michael@0: michael@0: const js::Class OuterWindowProxyClass = michael@0: PROXY_CLASS_WITH_EXT( michael@0: "Proxy", michael@0: 0, /* additional slots */ michael@0: 0, /* additional class flags */ michael@0: nullptr, /* call */ michael@0: nullptr, /* construct */ michael@0: PROXY_MAKE_EXT( michael@0: nullptr, /* outerObject */ michael@0: js::proxy_innerObject, michael@0: nullptr, /* iteratorObject */ michael@0: false /* isWrappedNative */ michael@0: )); michael@0: michael@0: bool michael@0: nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle proxy, michael@0: bool *extensible) michael@0: { michael@0: // If [[Extensible]] could be false, then navigating a window could navigate michael@0: // to a window that's [[Extensible]] after being at one that wasn't: an michael@0: // invariant violation. So always report true for this. michael@0: *extensible = true; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::preventExtensions(JSContext *cx, michael@0: JS::Handle proxy) michael@0: { michael@0: // See above. michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_CANT_CHANGE_EXTENSIBILITY); michael@0: return false; michael@0: } michael@0: michael@0: const char * michael@0: nsOuterWindowProxy::className(JSContext *cx, JS::Handle proxy) michael@0: { michael@0: MOZ_ASSERT(js::IsProxy(proxy)); michael@0: michael@0: return "Window"; michael@0: } michael@0: michael@0: void michael@0: nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy) michael@0: { michael@0: nsGlobalWindow* global = GetWindow(proxy); michael@0: if (global) { michael@0: global->ClearWrapper(); michael@0: michael@0: // Ideally we would use OnFinalize here, but it's possible that michael@0: // EnsureScriptEnvironment will later be called on the window, and we don't michael@0: // want to create a new script object in that case. Therefore, we need to michael@0: // write a non-null value that will reliably crash when dereferenced. michael@0: global->PoisonOuterWindowProxy(proxy); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::getPropertyDescriptor(JSContext* cx, michael@0: JS::Handle proxy, michael@0: JS::Handle id, michael@0: JS::MutableHandle desc) michael@0: { michael@0: // The only thing we can do differently from js::Wrapper is shadow stuff with michael@0: // our indexed properties, so we can just try getOwnPropertyDescriptor and if michael@0: // that gives us nothing call on through to js::Wrapper. michael@0: desc.object().set(nullptr); michael@0: if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) { michael@0: return false; michael@0: } michael@0: michael@0: if (desc.object()) { michael@0: return true; michael@0: } michael@0: michael@0: return js::Wrapper::getPropertyDescriptor(cx, proxy, id, desc); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::getOwnPropertyDescriptor(JSContext* cx, michael@0: JS::Handle proxy, michael@0: JS::Handle id, michael@0: JS::MutableHandle desc) michael@0: { michael@0: bool found; michael@0: if (!GetSubframeWindow(cx, proxy, id, desc.value(), found)) { michael@0: return false; michael@0: } michael@0: if (found) { michael@0: FillPropertyDescriptor(desc, proxy, true); michael@0: return true; michael@0: } michael@0: // else fall through to js::Wrapper michael@0: michael@0: return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::defineProperty(JSContext* cx, michael@0: JS::Handle proxy, michael@0: JS::Handle id, michael@0: JS::MutableHandle desc) michael@0: { michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: // Spec says to Reject whether this is a supported index or not, michael@0: // since we have no indexed setter or indexed creator. That means michael@0: // throwing in strict mode (FIXME: Bug 828137), doing nothing in michael@0: // non-strict mode. michael@0: return true; michael@0: } michael@0: michael@0: return js::Wrapper::defineProperty(cx, proxy, id, desc); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::getOwnPropertyNames(JSContext *cx, michael@0: JS::Handle proxy, michael@0: JS::AutoIdVector &props) michael@0: { michael@0: // Just our indexed stuff followed by our "normal" own property names. michael@0: if (!AppendIndexedPropertyNames(cx, proxy, props)) { michael@0: return false; michael@0: } michael@0: michael@0: JS::AutoIdVector innerProps(cx); michael@0: if (!js::Wrapper::getOwnPropertyNames(cx, proxy, innerProps)) { michael@0: return false; michael@0: } michael@0: return js::AppendUnique(cx, props, innerProps); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::delete_(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id, bool *bp) michael@0: { michael@0: if (nsCOMPtr frame = GetSubframeWindow(cx, proxy, id)) { michael@0: // Reject (which means throw if strict, else return false) the delete. michael@0: // Except we don't even know whether we're strict. See bug 803157. michael@0: *bp = false; michael@0: return true; michael@0: } michael@0: michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: // Indexed, but not supported. Spec says return true. michael@0: *bp = true; michael@0: return true; michael@0: } michael@0: michael@0: return js::Wrapper::delete_(cx, proxy, id, bp); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::enumerate(JSContext *cx, JS::Handle proxy, michael@0: JS::AutoIdVector &props) michael@0: { michael@0: // Just our indexed stuff followed by our "normal" own property names. michael@0: if (!AppendIndexedPropertyNames(cx, proxy, props)) { michael@0: return false; michael@0: } michael@0: michael@0: JS::AutoIdVector innerProps(cx); michael@0: if (!js::Wrapper::enumerate(cx, proxy, innerProps)) { michael@0: return false; michael@0: } michael@0: return js::AppendUnique(cx, props, innerProps); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::has(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id, bool *bp) michael@0: { michael@0: if (nsCOMPtr frame = GetSubframeWindow(cx, proxy, id)) { michael@0: *bp = true; michael@0: return true; michael@0: } michael@0: michael@0: return js::Wrapper::has(cx, proxy, id, bp); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::hasOwn(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id, bool *bp) michael@0: { michael@0: if (nsCOMPtr frame = GetSubframeWindow(cx, proxy, id)) { michael@0: *bp = true; michael@0: return true; michael@0: } michael@0: michael@0: return js::Wrapper::hasOwn(cx, proxy, id, bp); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::get(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle receiver, michael@0: JS::Handle id, michael@0: JS::MutableHandle vp) michael@0: { michael@0: if (id == nsDOMClassInfo::sWrappedJSObject_id && michael@0: xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) { michael@0: vp.set(JS::ObjectValue(*proxy)); michael@0: return true; michael@0: } michael@0: michael@0: bool found; michael@0: if (!GetSubframeWindow(cx, proxy, id, vp, found)) { michael@0: return false; michael@0: } michael@0: if (found) { michael@0: return true; michael@0: } michael@0: // Else fall through to js::Wrapper michael@0: michael@0: return js::Wrapper::get(cx, proxy, receiver, id, vp); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::set(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle receiver, michael@0: JS::Handle id, michael@0: bool strict, michael@0: JS::MutableHandle vp) michael@0: { michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: // Reject (which means throw if and only if strict) the set. michael@0: if (strict) { michael@0: // XXXbz This needs to throw, but see bug 828137. michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: return js::Wrapper::set(cx, proxy, receiver, id, strict, vp); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::keys(JSContext *cx, JS::Handle proxy, michael@0: JS::AutoIdVector &props) michael@0: { michael@0: // BaseProxyHandler::keys seems to do what we want here: call michael@0: // getOwnPropertyNames and then filter out the non-enumerable properties. michael@0: return js::BaseProxyHandler::keys(cx, proxy, props); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::iterate(JSContext *cx, JS::Handle proxy, michael@0: unsigned flags, JS::MutableHandle vp) michael@0: { michael@0: // BaseProxyHandler::iterate seems to do what we want here: fall michael@0: // back on the property names returned from keys() and enumerate(). michael@0: return js::BaseProxyHandler::iterate(cx, proxy, flags, vp); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::GetSubframeWindow(JSContext *cx, michael@0: JS::Handle proxy, michael@0: JS::Handle id, michael@0: JS::MutableHandle vp, michael@0: bool& found) michael@0: { michael@0: nsCOMPtr frame = GetSubframeWindow(cx, proxy, id); michael@0: if (!frame) { michael@0: found = false; michael@0: return true; michael@0: } michael@0: michael@0: found = true; michael@0: // Just return the window's global michael@0: nsGlobalWindow* global = static_cast(frame.get()); michael@0: global->EnsureInnerWindow(); michael@0: JSObject* obj = global->FastGetGlobalJSObject(); michael@0: // This null check fixes a hard-to-reproduce crash that occurs when we michael@0: // get here when we're mid-call to nsDocShell::Destroy. See bug 640904 michael@0: // comment 105. michael@0: if (MOZ_UNLIKELY(!obj)) { michael@0: return xpc::Throw(cx, NS_ERROR_FAILURE); michael@0: } michael@0: michael@0: vp.setObject(*obj); michael@0: return JS_WrapValue(cx, vp); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsOuterWindowProxy::GetSubframeWindow(JSContext *cx, michael@0: JS::Handle proxy, michael@0: JS::Handle id) michael@0: { michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (!IsArrayIndex(index)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsGlobalWindow* win = GetWindow(proxy); michael@0: bool unused; michael@0: return win->IndexedGetter(index, unused); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy, michael@0: JS::AutoIdVector &props) michael@0: { michael@0: uint32_t length = GetWindow(proxy)->Length(); michael@0: MOZ_ASSERT(int32_t(length) >= 0); michael@0: if (!props.reserve(props.length() + length)) { michael@0: return false; michael@0: } michael@0: for (int32_t i = 0; i < int32_t(length); ++i) { michael@0: props.append(INT_TO_JSID(i)); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::watch(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id, JS::Handle callable) michael@0: { michael@0: return js::WatchGuts(cx, proxy, id, callable); michael@0: } michael@0: michael@0: bool michael@0: nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle proxy, michael@0: JS::Handle id) michael@0: { michael@0: return js::UnwatchGuts(cx, proxy, id); michael@0: } michael@0: michael@0: nsOuterWindowProxy michael@0: nsOuterWindowProxy::singleton; michael@0: michael@0: class nsChromeOuterWindowProxy : public nsOuterWindowProxy michael@0: { michael@0: public: michael@0: nsChromeOuterWindowProxy() : nsOuterWindowProxy() {} michael@0: michael@0: virtual const char *className(JSContext *cx, JS::Handle wrapper) MOZ_OVERRIDE; michael@0: michael@0: static nsChromeOuterWindowProxy singleton; michael@0: }; michael@0: michael@0: const char * michael@0: nsChromeOuterWindowProxy::className(JSContext *cx, michael@0: JS::Handle proxy) michael@0: { michael@0: MOZ_ASSERT(js::IsProxy(proxy)); michael@0: michael@0: return "ChromeWindow"; michael@0: } michael@0: michael@0: nsChromeOuterWindowProxy michael@0: nsChromeOuterWindowProxy::singleton; michael@0: michael@0: static JSObject* michael@0: NewOuterWindowProxy(JSContext *cx, JS::Handle parent, bool isChrome) michael@0: { michael@0: JSAutoCompartment ac(cx, parent); michael@0: js::WrapperOptions options; michael@0: options.setClass(&OuterWindowProxyClass); michael@0: options.setSingleton(true); michael@0: JSObject *obj = js::Wrapper::New(cx, parent, parent, michael@0: isChrome ? &nsChromeOuterWindowProxy::singleton michael@0: : &nsOuterWindowProxy::singleton, michael@0: &options); michael@0: michael@0: NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class"); michael@0: return obj; michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: //*** nsGlobalWindow: Object Management michael@0: //***************************************************************************** michael@0: michael@0: nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) michael@0: : nsPIDOMWindow(aOuterWindow), michael@0: mIdleFuzzFactor(0), michael@0: mIdleCallbackIndex(-1), michael@0: mCurrentlyIdle(false), michael@0: mAddActiveEventFuzzTime(true), michael@0: mIsFrozen(false), michael@0: mFullScreen(false), michael@0: mIsClosed(false), michael@0: mInClose(false), michael@0: mHavePendingClose(false), michael@0: mHadOriginalOpener(false), michael@0: mIsPopupSpam(false), michael@0: mBlockScriptedClosingFlag(false), michael@0: mFireOfflineStatusChangeEventOnThaw(false), michael@0: mNotifyIdleObserversIdleOnThaw(false), michael@0: mNotifyIdleObserversActiveOnThaw(false), michael@0: mCreatingInnerWindow(false), michael@0: mIsChrome(false), michael@0: mCleanMessageManager(false), michael@0: mNeedsFocus(true), michael@0: mHasFocus(false), michael@0: #if defined(XP_MACOSX) michael@0: mShowAccelerators(false), michael@0: mShowFocusRings(false), michael@0: #else michael@0: mShowAccelerators(true), michael@0: mShowFocusRings(true), michael@0: #endif michael@0: mShowFocusRingForContent(false), michael@0: mFocusByKeyOccurred(false), michael@0: mInnerObjectsFreed(false), michael@0: mHasGamepad(false), michael@0: #ifdef MOZ_GAMEPAD michael@0: mHasSeenGamepadInput(false), michael@0: #endif michael@0: mNotifiedIDDestroyed(false), michael@0: mAllowScriptsToClose(false), michael@0: mTimeoutInsertionPoint(nullptr), michael@0: mTimeoutPublicIdCounter(1), michael@0: mTimeoutFiringDepth(0), michael@0: mTimeoutsSuspendDepth(0), michael@0: mFocusMethod(0), michael@0: mSerial(0), michael@0: #ifdef DEBUG michael@0: mSetOpenerWindowCalled(false), michael@0: #endif michael@0: #ifdef MOZ_B2G michael@0: mNetworkUploadObserverEnabled(false), michael@0: mNetworkDownloadObserverEnabled(false), michael@0: #endif michael@0: mCleanedUp(false), michael@0: mDialogAbuseCount(0), michael@0: mAreDialogsEnabled(true) michael@0: { michael@0: nsLayoutStatics::AddRef(); michael@0: michael@0: // Initialize the PRCList (this). michael@0: PR_INIT_CLIST(this); michael@0: michael@0: if (aOuterWindow) { michael@0: // |this| is an inner window, add this inner window to the outer michael@0: // window list of inners. michael@0: PR_INSERT_AFTER(this, aOuterWindow); michael@0: michael@0: mObserver = new nsGlobalWindowObserver(this); michael@0: if (mObserver) { michael@0: NS_ADDREF(mObserver); michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: // Watch for online/offline status changes so we can fire events. Use michael@0: // a strong reference. michael@0: os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, michael@0: false); michael@0: michael@0: // Watch for dom-storage2-changed so we can fire storage michael@0: // events. Use a strong reference. michael@0: os->AddObserver(mObserver, "dom-storage2-changed", false); michael@0: } michael@0: } michael@0: } else { michael@0: // |this| is an outer window. Outer windows start out frozen and michael@0: // remain frozen until they get an inner window, so freeze this michael@0: // outer window here. michael@0: Freeze(); michael@0: michael@0: mObserver = nullptr; michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: // We could have failed the first time through trying michael@0: // to create the entropy collector, so we should michael@0: // try to get one until we succeed. michael@0: michael@0: gRefCnt++; michael@0: michael@0: if (gRefCnt == 1) { michael@0: Preferences::AddIntVarCache(&gMinTimeoutValue, michael@0: "dom.min_timeout_value", michael@0: DEFAULT_MIN_TIMEOUT_VALUE); michael@0: Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue, michael@0: "dom.min_background_timeout_value", michael@0: DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE); michael@0: Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled, michael@0: "dom.idle-observers-api.fuzz_time.disabled", michael@0: false); michael@0: } michael@0: michael@0: if (gDumpFile == nullptr) { michael@0: const nsAdoptingCString& fname = michael@0: Preferences::GetCString("browser.dom.window.dump.file"); michael@0: if (!fname.IsEmpty()) { michael@0: // if this fails to open, Dump() knows to just go to stdout michael@0: // on null. michael@0: gDumpFile = fopen(fname, "wb+"); michael@0: } else { michael@0: gDumpFile = stdout; michael@0: } michael@0: } michael@0: michael@0: mSerial = ++gSerialCounter; michael@0: michael@0: #ifdef DEBUG michael@0: if (!PR_GetEnv("MOZ_QUIET")) { michael@0: printf_stderr("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n", michael@0: gRefCnt, michael@0: static_cast(ToCanonicalSupports(this)), michael@0: getpid(), michael@0: gSerialCounter, michael@0: static_cast(ToCanonicalSupports(aOuterWindow))); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (gDOMLeakPRLog) michael@0: PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG, michael@0: ("DOMWINDOW %p created outer=%p", this, aOuterWindow)); michael@0: #endif michael@0: michael@0: NS_ASSERTION(sWindowsById, "Windows hash table must be created!"); michael@0: NS_ASSERTION(!sWindowsById->Get(mWindowID), michael@0: "This window shouldn't be in the hash table yet!"); michael@0: // We seem to see crashes in release builds because of null |sWindowsById|. michael@0: if (sWindowsById) { michael@0: sWindowsById->Put(mWindowID, this); michael@0: } michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: nsGlobalWindow::Init() michael@0: { michael@0: CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector); michael@0: NS_ASSERTION(gEntropyCollector, michael@0: "gEntropyCollector should have been initialized!"); michael@0: michael@0: #ifdef PR_LOGGING michael@0: gDOMLeakPRLog = PR_NewLogModule("DOMLeak"); michael@0: NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!"); michael@0: #endif michael@0: michael@0: sWindowsById = new WindowByIdTable(); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: DisconnectEventTargetObjects(nsPtrHashKey* aKey, michael@0: void* aClosure) michael@0: { michael@0: nsRefPtr target = aKey->GetKey(); michael@0: target->DisconnectFromOwner(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: nsGlobalWindow::~nsGlobalWindow() michael@0: { michael@0: mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr); michael@0: mEventTargetObjects.Clear(); michael@0: michael@0: // We have to check if sWindowsById isn't null because ::Shutdown might have michael@0: // been called. michael@0: if (sWindowsById) { michael@0: NS_ASSERTION(sWindowsById->Get(mWindowID), michael@0: "This window should be in the hash table"); michael@0: sWindowsById->Remove(mWindowID); michael@0: } michael@0: michael@0: --gRefCnt; michael@0: michael@0: #ifdef DEBUG michael@0: if (!PR_GetEnv("MOZ_QUIET")) { michael@0: nsAutoCString url; michael@0: if (mLastOpenedURI) { michael@0: mLastOpenedURI->GetSpec(url); michael@0: michael@0: // Data URLs can be very long, so truncate to avoid flooding the log. michael@0: const uint32_t maxURLLength = 1000; michael@0: if (url.Length() > maxURLLength) { michael@0: url.Truncate(maxURLLength); michael@0: } michael@0: } michael@0: michael@0: nsGlobalWindow* outer = static_cast(mOuterWindow.get()); michael@0: printf_stderr("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = %s]\n", michael@0: gRefCnt, michael@0: static_cast(ToCanonicalSupports(this)), michael@0: getpid(), michael@0: mSerial, michael@0: static_cast(ToCanonicalSupports(outer)), michael@0: url.get()); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (gDOMLeakPRLog) michael@0: PR_LOG(gDOMLeakPRLog, PR_LOG_DEBUG, michael@0: ("DOMWINDOW %p destroyed", this)); michael@0: #endif michael@0: michael@0: if (IsOuterWindow()) { michael@0: JSObject *proxy = GetWrapperPreserveColor(); michael@0: if (proxy) { michael@0: js::SetProxyExtra(proxy, 0, js::PrivateValue(nullptr)); michael@0: } michael@0: michael@0: // An outer window is destroyed with inner windows still possibly michael@0: // alive, iterate through the inner windows and null out their michael@0: // back pointer to this outer, and pull them out of the list of michael@0: // inner windows. michael@0: michael@0: nsGlobalWindow *w; michael@0: while ((w = (nsGlobalWindow *)PR_LIST_HEAD(this)) != this) { michael@0: PR_REMOVE_AND_INIT_LINK(w); michael@0: } michael@0: michael@0: DropOuterWindowDocs(); michael@0: } else { michael@0: Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, michael@0: mMutationBits ? 1 : 0); michael@0: michael@0: if (mListenerManager) { michael@0: mListenerManager->Disconnect(); michael@0: mListenerManager = nullptr; michael@0: } michael@0: michael@0: // An inner window is destroyed, pull it out of the outer window's michael@0: // list if inner windows. michael@0: michael@0: PR_REMOVE_LINK(this); michael@0: michael@0: // If our outer window's inner window is this window, null out the michael@0: // outer window's reference to this window that's being deleted. michael@0: nsGlobalWindow *outer = GetOuterWindowInternal(); michael@0: if (outer) { michael@0: outer->MaybeClearInnerWindow(this); michael@0: } michael@0: } michael@0: michael@0: // Outer windows are always supposed to call CleanUp before letting themselves michael@0: // be destroyed. And while CleanUp generally seems to be intended to clean up michael@0: // outers, we've historically called it for both. Changing this would probably michael@0: // involve auditing all of the references that inners and outers can have, and michael@0: // separating the handling into CleanUp() and FreeInnerObjects. michael@0: if (IsInnerWindow()) { michael@0: CleanUp(); michael@0: } else { michael@0: MOZ_ASSERT(mCleanedUp); michael@0: } michael@0: michael@0: nsCOMPtr ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); michael@0: if (ac) michael@0: ac->RemoveWindowAsListener(this); michael@0: michael@0: nsLayoutStatics::Release(); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::AddEventTargetObject(DOMEventTargetHelper* aObject) michael@0: { michael@0: mEventTargetObjects.PutEntry(aObject); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject) michael@0: { michael@0: mEventTargetObjects.RemoveEntry(aObject); michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsGlobalWindow::ShutDown() michael@0: { michael@0: if (gDumpFile && gDumpFile != stdout) { michael@0: fclose(gDumpFile); michael@0: } michael@0: gDumpFile = nullptr; michael@0: michael@0: NS_IF_RELEASE(gEntropyCollector); michael@0: michael@0: delete sWindowsById; michael@0: sWindowsById = nullptr; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow) michael@0: { michael@0: if (aWindow->mCachedXBLPrototypeHandlers && michael@0: aWindow->mCachedXBLPrototypeHandlers->Count() > 0) { michael@0: aWindow->mCachedXBLPrototypeHandlers->Clear(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::MaybeForgiveSpamCount() michael@0: { michael@0: if (IsOuterWindow() && michael@0: IsPopupSpamWindow()) michael@0: { michael@0: SetPopupSpamWindow(false); michael@0: --gOpenPopupSpamCount; michael@0: NS_ASSERTION(gOpenPopupSpamCount >= 0, michael@0: "Unbalanced decrement of gOpenPopupSpamCount"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::DropOuterWindowDocs() michael@0: { michael@0: MOZ_ASSERT(IsOuterWindow()); michael@0: MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed()); michael@0: mDoc = nullptr; michael@0: mSuspendedDoc = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::CleanUp() michael@0: { michael@0: // Guarantee idempotence. michael@0: if (mCleanedUp) michael@0: return; michael@0: mCleanedUp = true; michael@0: michael@0: mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr); michael@0: mEventTargetObjects.Clear(); michael@0: michael@0: if (mObserver) { michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC); michael@0: os->RemoveObserver(mObserver, "dom-storage2-changed"); michael@0: } michael@0: michael@0: #ifdef MOZ_B2G michael@0: DisableNetworkEvent(NS_NETWORK_UPLOAD_EVENT); michael@0: DisableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT); michael@0: #endif // MOZ_B2G michael@0: michael@0: if (mIdleService) { michael@0: mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S); michael@0: } michael@0: michael@0: // Drop its reference to this dying window, in case for some bogus reason michael@0: // the object stays around. michael@0: mObserver->Forget(); michael@0: NS_RELEASE(mObserver); michael@0: } michael@0: michael@0: if (mNavigator) { michael@0: mNavigator->Invalidate(); michael@0: mNavigator = nullptr; michael@0: } michael@0: michael@0: mScreen = nullptr; michael@0: mMenubar = nullptr; michael@0: mToolbar = nullptr; michael@0: mLocationbar = nullptr; michael@0: mPersonalbar = nullptr; michael@0: mStatusbar = nullptr; michael@0: mScrollbars = nullptr; michael@0: mLocation = nullptr; michael@0: mHistory = nullptr; michael@0: mFrames = nullptr; michael@0: mWindowUtils = nullptr; michael@0: mApplicationCache = nullptr; michael@0: mIndexedDB = nullptr; michael@0: michael@0: mConsole = nullptr; michael@0: michael@0: mExternal = nullptr; michael@0: michael@0: mPerformance = nullptr; michael@0: michael@0: #ifdef MOZ_WEBSPEECH michael@0: mSpeechSynthesis = nullptr; michael@0: #endif michael@0: michael@0: ClearControllers(); michael@0: michael@0: mOpener = nullptr; // Forces Release michael@0: if (mContext) { michael@0: mContext = nullptr; // Forces Release michael@0: } michael@0: mChromeEventHandler = nullptr; // Forces Release michael@0: mParentTarget = nullptr; michael@0: michael@0: nsGlobalWindow *inner = GetCurrentInnerWindowInternal(); michael@0: michael@0: if (inner) { michael@0: inner->CleanUp(); michael@0: } michael@0: michael@0: DisableGamepadUpdates(); michael@0: mHasGamepad = false; michael@0: michael@0: if (mCleanMessageManager) { michael@0: NS_ABORT_IF_FALSE(mIsChrome, "only chrome should have msg manager cleaned"); michael@0: nsGlobalChromeWindow *asChrome = static_cast(this); michael@0: if (asChrome->mMessageManager) { michael@0: static_cast( michael@0: asChrome->mMessageManager.get())->Disconnect(); michael@0: } michael@0: } michael@0: michael@0: mArguments = nullptr; michael@0: mDialogArguments = nullptr; michael@0: michael@0: CleanupCachedXBLHandlers(this); michael@0: michael@0: for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { michael@0: mAudioContexts[i]->Shutdown(); michael@0: } michael@0: mAudioContexts.Clear(); michael@0: michael@0: if (mIdleTimer) { michael@0: mIdleTimer->Cancel(); michael@0: mIdleTimer = nullptr; michael@0: } michael@0: michael@0: DisableTimeChangeNotifications(); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::ClearControllers() michael@0: { michael@0: if (mControllers) { michael@0: uint32_t count; michael@0: mControllers->GetControllerCount(&count); michael@0: michael@0: while (count--) { michael@0: nsCOMPtr controller; michael@0: mControllers->GetControllerAt(count, getter_AddRefs(controller)); michael@0: michael@0: nsCOMPtr context = do_QueryInterface(controller); michael@0: if (context) michael@0: context->SetCommandContext(nullptr); michael@0: } michael@0: michael@0: mControllers = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::FreeInnerObjects() michael@0: { michael@0: NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window"); michael@0: michael@0: // Make sure that this is called before we null out the document and michael@0: // other members that the window destroyed observers could michael@0: // re-create. michael@0: NotifyDOMWindowDestroyed(this); michael@0: michael@0: mInnerObjectsFreed = true; michael@0: michael@0: // Kill all of the workers for this window. michael@0: mozilla::dom::workers::CancelWorkersForWindow(this); michael@0: michael@0: // Close all offline storages for this window. michael@0: quota::QuotaManager* quotaManager = quota::QuotaManager::Get(); michael@0: if (quotaManager) { michael@0: quotaManager->AbortCloseStoragesForWindow(this); michael@0: } michael@0: michael@0: ClearAllTimeouts(); michael@0: michael@0: if (mIdleTimer) { michael@0: mIdleTimer->Cancel(); michael@0: mIdleTimer = nullptr; michael@0: } michael@0: michael@0: mIdleObservers.Clear(); michael@0: michael@0: mChromeEventHandler = nullptr; michael@0: michael@0: if (mListenerManager) { michael@0: mListenerManager->Disconnect(); michael@0: mListenerManager = nullptr; michael@0: } michael@0: michael@0: mLocation = nullptr; michael@0: mHistory = nullptr; michael@0: michael@0: if (mNavigator) { michael@0: mNavigator->OnNavigation(); michael@0: mNavigator->Invalidate(); michael@0: mNavigator = nullptr; michael@0: } michael@0: michael@0: if (mScreen) { michael@0: mScreen = nullptr; michael@0: } michael@0: michael@0: if (mDoc) { michael@0: // Remember the document's principal and URI. michael@0: mDocumentPrincipal = mDoc->NodePrincipal(); michael@0: mDocumentURI = mDoc->GetDocumentURI(); michael@0: mDocBaseURI = mDoc->GetDocBaseURI(); michael@0: michael@0: while (mDoc->EventHandlingSuppressed()) { michael@0: mDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, false); michael@0: } michael@0: michael@0: // Note: we don't have to worry about eAnimationsOnly suppressions because michael@0: // they won't leak. michael@0: } michael@0: michael@0: // Remove our reference to the document and the document principal. michael@0: mFocusedNode = nullptr; michael@0: michael@0: if (mApplicationCache) { michael@0: static_cast(mApplicationCache.get())->Disconnect(); michael@0: mApplicationCache = nullptr; michael@0: } michael@0: michael@0: mIndexedDB = nullptr; michael@0: michael@0: NotifyWindowIDDestroyed("inner-window-destroyed"); michael@0: michael@0: CleanupCachedXBLHandlers(this); michael@0: michael@0: for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { michael@0: mAudioContexts[i]->Shutdown(); michael@0: } michael@0: mAudioContexts.Clear(); michael@0: michael@0: #ifdef MOZ_GAMEPAD michael@0: DisableGamepadUpdates(); michael@0: mHasGamepad = false; michael@0: mGamepads.Clear(); michael@0: #endif michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // nsGlobalWindow::nsISupports michael@0: //***************************************************************************** michael@0: michael@0: DOMCI_DATA(Window, nsGlobalWindow) michael@0: michael@0: // QueryInterface implementation for nsGlobalWindow michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: // Make sure this matches the cast in nsGlobalWindow::FromWrapper() michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMWindow) michael@0: #ifdef MOZ_B2G michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMWindowB2G) michael@0: #endif // MOZ_B2G michael@0: #ifdef MOZ_WEBSPEECH michael@0: NS_INTERFACE_MAP_ENTRY(nsISpeechSynthesisGetter) michael@0: #endif // MOZ_B2G michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMJSWindow) michael@0: if (aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) { michael@0: foundInterface = static_cast(this); michael@0: if (!sWarnedAboutWindowInternal) { michael@0: sWarnedAboutWindowInternal = true; michael@0: nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, michael@0: NS_LITERAL_CSTRING("Extensions"), mDoc, michael@0: nsContentUtils::eDOM_PROPERTIES, michael@0: "nsIDOMWindowInternalWarning"); michael@0: } michael@0: } else michael@0: NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) michael@0: NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject) michael@0: NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) michael@0: NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) michael@0: NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMWindowPerformance) michael@0: NS_INTERFACE_MAP_ENTRY(nsITouchEventReceiver) michael@0: NS_INTERFACE_MAP_ENTRY(nsIInlineEventHandlers) michael@0: NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow) michael@0: michael@0: static PLDHashOperator michael@0: MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap& aData, void* aClosure) michael@0: { michael@0: JS::ExposeObjectToActiveJS(aData); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow) michael@0: if (tmp->IsBlackForCC(false)) { michael@0: if (tmp->mCachedXBLPrototypeHandlers) { michael@0: tmp->mCachedXBLPrototypeHandlers->Enumerate(MarkXBLHandlers, nullptr); michael@0: } michael@0: if (EventListenerManager* elm = tmp->GetExistingListenerManager()) { michael@0: elm->MarkForCC(); michael@0: } michael@0: tmp->UnmarkGrayTimers(); michael@0: return true; michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow) michael@0: return tmp->IsBlackForCC(true); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindow) michael@0: return tmp->IsBlackForCC(false); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END michael@0: michael@0: inline void michael@0: ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, michael@0: IdleObserverHolder& aField, michael@0: const char* aName, michael@0: unsigned aFlags) michael@0: { michael@0: CycleCollectionNoteChild(aCallback, aField.mIdleObserver.get(), aName, aFlags); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindow) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow) michael@0: if (MOZ_UNLIKELY(cb.WantDebugInfo())) { michael@0: char name[512]; michael@0: PR_snprintf(name, sizeof(name), "nsGlobalWindow #%ld", tmp->mWindowID); michael@0: cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); michael@0: } else { michael@0: NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get()) michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) michael@0: michael@0: #ifdef MOZ_WEBSPEECH michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis) michael@0: #endif michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) michael@0: michael@0: for (nsTimeout* timeout = tmp->mTimeouts.getFirst(); michael@0: timeout; michael@0: timeout = timeout->getNext()) { michael@0: cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout)); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers) michael@0: michael@0: #ifdef MOZ_GAMEPAD michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads) michael@0: #endif michael@0: michael@0: // Traverse stuff from nsPIDOMWindow michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedNode) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) michael@0: nsGlobalWindow::CleanupCachedXBLHandlers(tmp); michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) michael@0: michael@0: #ifdef MOZ_WEBSPEECH michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis) michael@0: #endif michael@0: michael@0: if (tmp->mOuterWindow) { michael@0: static_cast(tmp->mOuterWindow.get())->MaybeClearInnerWindow(tmp); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow) michael@0: } michael@0: michael@0: if (tmp->mListenerManager) { michael@0: tmp->mListenerManager->Disconnect(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager) michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage) michael@0: if (tmp->mApplicationCache) { michael@0: static_cast(tmp->mApplicationCache.get())->Disconnect(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache) michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStorageEvents) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers) michael@0: michael@0: #ifdef MOZ_GAMEPAD michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads) michael@0: #endif michael@0: michael@0: // Unlink stuff from nsPIDOMWindow michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedNode) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: nsGlobalWindow::RiskyUnlink() michael@0: { michael@0: NS_CYCLE_COLLECTION_INNERNAME.Unlink(this); michael@0: } michael@0: #endif michael@0: michael@0: struct TraceData michael@0: { michael@0: const TraceCallbacks& callbacks; michael@0: void* closure; michael@0: }; michael@0: michael@0: static PLDHashOperator michael@0: TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap& aData, void* aClosure) michael@0: { michael@0: TraceData* data = static_cast(aClosure); michael@0: data->callbacks.Trace(&aData, "Cached XBL prototype handler", data->closure); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow) michael@0: if (tmp->mCachedXBLPrototypeHandlers) { michael@0: TraceData data = { aCallbacks, aClosure }; michael@0: tmp->mCachedXBLPrototypeHandlers->Enumerate(TraceXBLHandlers, &data); michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: bool michael@0: nsGlobalWindow::IsBlackForCC(bool aTracingNeeded) michael@0: { michael@0: if (!nsCCUncollectableMarker::sGeneration) { michael@0: return false; michael@0: } michael@0: michael@0: return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) || michael@0: IsBlack()) && michael@0: (!aTracingNeeded || michael@0: HasNothingToTrace(static_cast(this))); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::UnmarkGrayTimers() michael@0: { michael@0: for (nsTimeout* timeout = mTimeouts.getFirst(); michael@0: timeout; michael@0: timeout = timeout->getNext()) { michael@0: if (timeout->mScriptHandler) { michael@0: Function* f = timeout->mScriptHandler->GetCallback(); michael@0: if (f) { michael@0: // Callable() already does xpc_UnmarkGrayObject. michael@0: DebugOnly > o = f->Callable(); michael@0: MOZ_ASSERT(!xpc_IsGrayGCThing(o.value), "Should have been unmarked"); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // nsGlobalWindow::nsIScriptGlobalObject michael@0: //***************************************************************************** michael@0: michael@0: nsresult michael@0: nsGlobalWindow::EnsureScriptEnvironment() michael@0: { michael@0: nsGlobalWindow* outer = GetOuterWindowInternal(); michael@0: if (!outer) { michael@0: NS_WARNING("No outer window available!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (outer->GetWrapperPreserveColor()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(), michael@0: "No cached wrapper, but we have an inner window?"); michael@0: michael@0: // If this window is a [i]frame, don't bother GC'ing when the frame's context michael@0: // is destroyed since a GC will happen when the frameset or host document is michael@0: // destroyed anyway. michael@0: nsCOMPtr context = new nsJSContext(!IsFrame(), outer); michael@0: michael@0: NS_ASSERTION(!outer->mContext, "Will overwrite mContext!"); michael@0: michael@0: // should probably assert the context is clean??? michael@0: context->WillInitializeContext(); michael@0: michael@0: nsresult rv = context->InitContext(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: outer->mContext = context; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIScriptContext * michael@0: nsGlobalWindow::GetScriptContext() michael@0: { michael@0: nsGlobalWindow* outer = GetOuterWindowInternal(); michael@0: return outer ? outer->mContext : nullptr; michael@0: } michael@0: michael@0: JSObject * michael@0: nsGlobalWindow::GetGlobalJSObject() michael@0: { michael@0: return FastGetGlobalJSObject(); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc) michael@0: { michael@0: TraceWrapper(aTrc, "active window global"); michael@0: } michael@0: michael@0: /* static */ michael@0: JSObject* michael@0: nsGlobalWindow::OuterObject(JSContext* aCx, JS::Handle aObj) michael@0: { michael@0: nsGlobalWindow* origWin; michael@0: UNWRAP_OBJECT(Window, aObj, origWin); michael@0: nsGlobalWindow* win = origWin->GetOuterWindowInternal(); michael@0: michael@0: if (!win) { michael@0: // If we no longer have an outer window. No code should ever be michael@0: // running on a window w/o an outer, which means this hook should michael@0: // never be called when we have no outer. But just in case, return michael@0: // null to prevent leaking an inner window to code in a different michael@0: // window. michael@0: NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: JS::Rooted winObj(aCx, win->FastGetGlobalJSObject()); michael@0: MOZ_ASSERT(winObj); michael@0: michael@0: // Note that while |wrapper| is same-compartment with cx, the outer window michael@0: // might not be. If we're running script in an inactive scope and evalute michael@0: // |this|, the outer window is actually a cross-compartment wrapper. So we michael@0: // need to wrap here. michael@0: if (!JS_WrapObject(aCx, &winObj)) { michael@0: NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return winObj; michael@0: } michael@0: michael@0: bool michael@0: nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument) michael@0: { michael@0: // We reuse the inner window when: michael@0: // a. We are currently at our original document. michael@0: // b. At least one of the following conditions are true: michael@0: // -- The new document is the same as the old document. This means that we're michael@0: // getting called from document.open(). michael@0: // -- The new document has the same origin as what we have loaded right now. michael@0: michael@0: if (!mDoc || !aNewDocument) { michael@0: return false; michael@0: } michael@0: michael@0: if (!mDoc->IsInitialDocument()) { michael@0: return false; michael@0: } michael@0: michael@0: NS_ASSERTION(NS_IsAboutBlank(mDoc->GetDocumentURI()), michael@0: "How'd this happen?"); michael@0: michael@0: // Great, we're the original document, check for one of the other michael@0: // conditions. michael@0: michael@0: if (mDoc == aNewDocument) { michael@0: return true; michael@0: } michael@0: michael@0: bool equal; michael@0: if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(), michael@0: &equal)) && michael@0: equal) { michael@0: // The origin is the same. michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::SetInitialPrincipalToSubject() michael@0: { michael@0: FORWARD_TO_OUTER_VOID(SetInitialPrincipalToSubject, ()); michael@0: michael@0: // First, grab the subject principal. michael@0: nsCOMPtr newWindowPrincipal = nsContentUtils::GetSubjectPrincipal(); michael@0: if (!newWindowPrincipal) { michael@0: newWindowPrincipal = nsContentUtils::GetSystemPrincipal(); michael@0: } michael@0: michael@0: // Now, if we're about to use the system principal or an nsExpandedPrincipal, michael@0: // make sure we're not using it for a content docshell. michael@0: if (nsContentUtils::IsSystemOrExpandedPrincipal(newWindowPrincipal) && michael@0: GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome) { michael@0: newWindowPrincipal = nullptr; michael@0: } michael@0: michael@0: // If there's an existing document, bail if it either: michael@0: if (mDoc) { michael@0: // (a) is not an initial about:blank document, or michael@0: if (!mDoc->IsInitialDocument()) michael@0: return; michael@0: // (b) already has the correct principal. michael@0: if (mDoc->NodePrincipal() == newWindowPrincipal) michael@0: return; michael@0: michael@0: #ifdef DEBUG michael@0: // If we have a document loaded at this point, it had better be about:blank. michael@0: // Otherwise, something is really weird. michael@0: nsCOMPtr uri; michael@0: mDoc->NodePrincipal()->GetURI(getter_AddRefs(uri)); michael@0: NS_ASSERTION(uri && NS_IsAboutBlank(uri) && michael@0: NS_IsAboutBlank(mDoc->GetDocumentURI()), michael@0: "Unexpected original document"); michael@0: #endif michael@0: } michael@0: michael@0: GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal); michael@0: mDoc->SetIsInitialDocument(true); michael@0: michael@0: nsCOMPtr shell = GetDocShell()->GetPresShell(); michael@0: michael@0: if (shell && !shell->DidInitialize()) { michael@0: // Ensure that if someone plays with this document they will get michael@0: // layout happening. michael@0: nsRect r = shell->GetPresContext()->GetVisibleArea(); michael@0: shell->Initialize(r.width, r.height); michael@0: } michael@0: } michael@0: michael@0: PopupControlState michael@0: PushPopupControlState(PopupControlState aState, bool aForce) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: PopupControlState oldState = gPopupControlState; michael@0: michael@0: if (aState < gPopupControlState || aForce) { michael@0: gPopupControlState = aState; michael@0: } michael@0: michael@0: return oldState; michael@0: } michael@0: michael@0: void michael@0: PopPopupControlState(PopupControlState aState) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: gPopupControlState = aState; michael@0: } michael@0: michael@0: PopupControlState michael@0: nsGlobalWindow::PushPopupControlState(PopupControlState aState, michael@0: bool aForce) const michael@0: { michael@0: return ::PushPopupControlState(aState, aForce); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::PopPopupControlState(PopupControlState aState) const michael@0: { michael@0: ::PopPopupControlState(aState); michael@0: } michael@0: michael@0: PopupControlState michael@0: nsGlobalWindow::GetPopupControlState() const michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: return gPopupControlState; michael@0: } michael@0: michael@0: #define WINDOWSTATEHOLDER_IID \ michael@0: {0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}} michael@0: michael@0: class WindowStateHolder MOZ_FINAL : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID) michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: WindowStateHolder(nsGlobalWindow *aWindow); michael@0: michael@0: nsGlobalWindow* GetInnerWindow() { return mInnerWindow; } michael@0: michael@0: void DidRestoreWindow() michael@0: { michael@0: mInnerWindow = nullptr; michael@0: } michael@0: michael@0: protected: michael@0: ~WindowStateHolder(); michael@0: michael@0: nsRefPtr mInnerWindow; michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID) michael@0: michael@0: WindowStateHolder::WindowStateHolder(nsGlobalWindow* aWindow) michael@0: : mInnerWindow(aWindow) michael@0: { michael@0: NS_PRECONDITION(aWindow, "null window"); michael@0: NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window"); michael@0: michael@0: // We hold onto this to make sure the inner window doesn't go away. The outer michael@0: // window ends up recalculating it anyway. michael@0: mInnerWindow->PreserveWrapper(ToSupports(mInnerWindow)); michael@0: michael@0: aWindow->SuspendTimeouts(); michael@0: michael@0: // When a global goes into the bfcache, we disable script. michael@0: xpc::Scriptability::Get(aWindow->GetWrapperPreserveColor()).SetDocShellAllowsScript(false); michael@0: } michael@0: michael@0: WindowStateHolder::~WindowStateHolder() michael@0: { michael@0: if (mInnerWindow) { michael@0: // This window was left in the bfcache and is now going away. We need to michael@0: // free it up. michael@0: // Note that FreeInnerObjects may already have been called on the michael@0: // inner window if its outer has already had SetDocShell(null) michael@0: // called. michael@0: mInnerWindow->FreeInnerObjects(); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder) michael@0: michael@0: // We need certain special behavior for remote XUL whitelisted domains, but we michael@0: // don't want that behavior to take effect in automation, because we whitelist michael@0: // all the mochitest domains. So we need to check a pref here. michael@0: static bool michael@0: TreatAsRemoteXUL(nsIPrincipal* aPrincipal) michael@0: { michael@0: MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(aPrincipal)); michael@0: return nsContentUtils::AllowXULXBLForPrincipal(aPrincipal) && michael@0: !Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false); michael@0: } michael@0: michael@0: /** michael@0: * Create a new global object that will be used for an inner window. michael@0: * Return the native global and an nsISupports 'holder' that can be used michael@0: * to manage the lifetime of it. michael@0: */ michael@0: static nsresult michael@0: CreateNativeGlobalForInner(JSContext* aCx, michael@0: nsGlobalWindow* aNewInner, michael@0: nsIURI* aURI, michael@0: nsIPrincipal* aPrincipal, michael@0: JS::MutableHandle aGlobal) michael@0: { michael@0: MOZ_ASSERT(aCx); michael@0: MOZ_ASSERT(aNewInner); michael@0: MOZ_ASSERT(aNewInner->IsInnerWindow()); michael@0: MOZ_ASSERT(aPrincipal); michael@0: michael@0: // DOMWindow with nsEP is not supported, we have to make sure michael@0: // no one creates one accidentally. michael@0: nsCOMPtr nsEP = do_QueryInterface(aPrincipal); michael@0: MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported"); michael@0: michael@0: nsGlobalWindow *top = nullptr; michael@0: if (aNewInner->GetOuterWindow()) { michael@0: top = aNewInner->GetTop(); michael@0: } michael@0: JS::CompartmentOptions options; michael@0: if (top) { michael@0: if (top->GetGlobalJSObject()) { michael@0: options.setSameZoneAs(top->GetGlobalJSObject()); michael@0: } michael@0: } michael@0: michael@0: nsIXPConnect* xpc = nsContentUtils::XPConnect(); michael@0: michael@0: // Determine if we need the Components object. michael@0: bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) || michael@0: TreatAsRemoteXUL(aPrincipal); michael@0: uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT; michael@0: flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK; michael@0: michael@0: nsCOMPtr holder; michael@0: nsresult rv = xpc->InitClassesWithNewWrappedGlobal( michael@0: aCx, ToSupports(aNewInner), michael@0: aPrincipal, flags, options, getter_AddRefs(holder)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aGlobal.set(holder->GetJSObject()); michael@0: michael@0: // Set the location information for the new global, so that tools like michael@0: // about:memory may use that information michael@0: MOZ_ASSERT(aGlobal); michael@0: MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal); michael@0: xpc::SetLocationForGlobal(aGlobal, aURI); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, michael@0: nsISupports* aState, michael@0: bool aForceReuseInnerWindow) michael@0: { michael@0: NS_PRECONDITION(mDocumentPrincipal == nullptr, michael@0: "mDocumentPrincipal prematurely set!"); michael@0: MOZ_ASSERT(aDocument); michael@0: michael@0: if (IsInnerWindow()) { michael@0: if (!mOuterWindow) { michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: } michael@0: michael@0: // Refuse to set a new document if the call came from an inner michael@0: // window that's not the current inner window. michael@0: if (mOuterWindow->GetCurrentInnerWindow() != this) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: return GetOuterWindowInternal()->SetNewDocument(aDocument, aState, michael@0: aForceReuseInnerWindow); michael@0: } michael@0: michael@0: NS_PRECONDITION(IsOuterWindow(), "Must only be called on outer windows"); michael@0: michael@0: if (IsFrozen()) { michael@0: // This outer is now getting its first inner, thaw the outer now michael@0: // that it's ready and is getting an inner window. michael@0: michael@0: Thaw(); michael@0: } michael@0: michael@0: NS_ASSERTION(!GetCurrentInnerWindow() || michael@0: GetCurrentInnerWindow()->GetExtantDoc() == mDoc, michael@0: "Uh, mDoc doesn't match the current inner window " michael@0: "document!"); michael@0: michael@0: bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument); michael@0: if (aForceReuseInnerWindow && michael@0: !wouldReuseInnerWindow && michael@0: mDoc && michael@0: mDoc->NodePrincipal() != aDocument->NodePrincipal()) { michael@0: NS_ERROR("Attempted forced inner window reuse while changing principal"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsCOMPtr oldDoc = mDoc; michael@0: michael@0: nsIScriptContext *scx = GetContextInternal(); michael@0: NS_ENSURE_TRUE(scx, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: JSContext *cx = scx->GetNativeContext(); michael@0: michael@0: #ifndef MOZ_DISABLE_CRYPTOLEGACY michael@0: // clear smartcard events, our document has gone away. michael@0: if (mCrypto && XRE_GetProcessType() != GeckoProcessType_Content) { michael@0: nsresult rv = mCrypto->SetEnableSmartCardEvents(false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: #endif michael@0: michael@0: if (!mDoc) { michael@0: // First document load. michael@0: michael@0: // Get our private root. If it is equal to us, then we need to michael@0: // attach our global key bindings that handles browser scrolling michael@0: // and other browser commands. michael@0: nsIDOMWindow* privateRoot = nsGlobalWindow::GetPrivateRoot(); michael@0: michael@0: if (privateRoot == static_cast(this)) { michael@0: nsXBLService::AttachGlobalKeyHandler(mChromeEventHandler); michael@0: } michael@0: } michael@0: michael@0: /* No mDocShell means we're already been partially closed down. When that michael@0: happens, setting status isn't a big requirement, so don't. (Doesn't happen michael@0: under normal circumstances, but bug 49615 describes a case.) */ michael@0: michael@0: nsContentUtils::AddScriptRunner( michael@0: NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus)); michael@0: michael@0: // Sometimes, WouldReuseInnerWindow() returns true even if there's no inner michael@0: // window (see bug 776497). Be safe. michael@0: bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) && michael@0: GetCurrentInnerWindowInternal(); michael@0: michael@0: nsresult rv = NS_OK; michael@0: michael@0: // Set mDoc even if this is an outer window to avoid michael@0: // having to *always* reach into the inner window to find the michael@0: // document. michael@0: mDoc = aDocument; michael@0: if (IsInnerWindow() && IsDOMBinding()) { michael@0: WindowBinding::ClearCachedDocumentValue(cx, this); michael@0: } michael@0: michael@0: // Take this opportunity to clear mSuspendedDoc. Our old inner window is now michael@0: // responsible for unsuspending it. michael@0: mSuspendedDoc = nullptr; michael@0: michael@0: #ifdef DEBUG michael@0: mLastOpenedURI = aDocument->GetDocumentURI(); michael@0: #endif michael@0: michael@0: mContext->WillInitializeContext(); michael@0: michael@0: nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); michael@0: michael@0: if (currentInner && currentInner->mNavigator) { michael@0: currentInner->mNavigator->OnNavigation(); michael@0: } michael@0: michael@0: nsRefPtr newInnerWindow; michael@0: bool createdInnerWindow = false; michael@0: michael@0: bool thisChrome = IsChromeWindow(); michael@0: michael@0: nsCxPusher cxPusher; michael@0: cxPusher.Push(cx); michael@0: michael@0: // Check if we're near the stack limit before we get anywhere near the michael@0: // transplanting code. michael@0: JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE); michael@0: michael@0: nsCOMPtr wsh = do_QueryInterface(aState); michael@0: NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?"); michael@0: michael@0: JS::Rooted newInnerGlobal(cx); michael@0: if (reUseInnerWindow) { michael@0: // We're reusing the current inner window. michael@0: NS_ASSERTION(!currentInner->IsFrozen(), michael@0: "We should never be reusing a shared inner window"); michael@0: newInnerWindow = currentInner; michael@0: newInnerGlobal = currentInner->GetWrapperPreserveColor(); michael@0: michael@0: if (aDocument != oldDoc) { michael@0: JS::ExposeObjectToActiveJS(newInnerGlobal); michael@0: } michael@0: michael@0: // We're reusing the inner window, but this still counts as a navigation, michael@0: // so all expandos and such defined on the outer window should go away. Force michael@0: // all Xray wrappers to be recomputed. michael@0: JS::Rooted rootedObject(cx, GetWrapperPreserveColor()); michael@0: JS::ExposeObjectToActiveJS(rootedObject); michael@0: if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Inner windows are only reused for same-origin principals, but the principals michael@0: // don't necessarily match exactly. Update the principal on the compartment to michael@0: // match the new document. michael@0: // NB: We don't just call currentInner->RefreshCompartmentPrincipals() here michael@0: // because we haven't yet set its mDoc to aDocument. michael@0: JSCompartment *compartment = js::GetObjectCompartment(newInnerGlobal); michael@0: #ifdef DEBUG michael@0: bool sameOrigin = false; michael@0: nsIPrincipal *existing = michael@0: nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment)); michael@0: aDocument->NodePrincipal()->Equals(existing, &sameOrigin); michael@0: MOZ_ASSERT(sameOrigin); michael@0: #endif michael@0: JS_SetCompartmentPrincipals(compartment, michael@0: nsJSPrincipals::get(aDocument->NodePrincipal())); michael@0: } else { michael@0: if (aState) { michael@0: newInnerWindow = wsh->GetInnerWindow(); michael@0: newInnerGlobal = newInnerWindow->GetWrapperPreserveColor(); michael@0: } else { michael@0: if (thisChrome) { michael@0: newInnerWindow = new nsGlobalChromeWindow(this); michael@0: } else if (mIsModalContentWindow) { michael@0: newInnerWindow = new nsGlobalModalWindow(this); michael@0: } else { michael@0: newInnerWindow = new nsGlobalWindow(this); michael@0: } michael@0: michael@0: // Freeze the outer window and null out the inner window so michael@0: // that initializing classes on the new inner doesn't end up michael@0: // reaching into the old inner window for classes etc. michael@0: // michael@0: // [This happens with Object.prototype when XPConnect creates michael@0: // a temporary global while initializing classes; the reason michael@0: // being that xpconnect creates the temp global w/o a parent michael@0: // and proto, which makes the JS engine look up classes in michael@0: // cx->globalObject, i.e. this outer window]. michael@0: michael@0: mInnerWindow = nullptr; michael@0: michael@0: Freeze(); michael@0: mCreatingInnerWindow = true; michael@0: // Every script context we are initialized with must create a michael@0: // new global. michael@0: rv = CreateNativeGlobalForInner(cx, newInnerWindow, michael@0: aDocument->GetDocumentURI(), michael@0: aDocument->NodePrincipal(), michael@0: &newInnerGlobal); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerGlobal && michael@0: newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal, michael@0: "Failed to get script global"); michael@0: michael@0: mCreatingInnerWindow = false; michael@0: createdInnerWindow = true; michael@0: Thaw(); michael@0: michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (currentInner && currentInner->GetWrapperPreserveColor()) { michael@0: if (oldDoc == aDocument) { michael@0: // Move the navigator from the old inner window to the new one since michael@0: // this is a document.write. This is safe from a same-origin point of michael@0: // view because document.write can only be used by the same origin. michael@0: newInnerWindow->mNavigator = currentInner->mNavigator; michael@0: currentInner->mNavigator = nullptr; michael@0: if (newInnerWindow->mNavigator) { michael@0: newInnerWindow->mNavigator->SetWindow(newInnerWindow); michael@0: } michael@0: michael@0: // Make a copy of the old window's performance object on document.open. michael@0: // Note that we have to force eager creation of it here, because we need michael@0: // to grab the current document channel and whatnot before that changes. michael@0: currentInner->CreatePerformanceObjectIfNeeded(); michael@0: if (currentInner->mPerformance) { michael@0: newInnerWindow->mPerformance = michael@0: new nsPerformance(newInnerWindow, michael@0: currentInner->mPerformance->GetDOMTiming(), michael@0: currentInner->mPerformance->GetChannel(), michael@0: currentInner->mPerformance->GetParentPerformance()); michael@0: } michael@0: } michael@0: michael@0: // Don't free objects on our current inner window if it's going to be michael@0: // held in the bfcache. michael@0: if (!currentInner->IsFrozen()) { michael@0: currentInner->FreeInnerObjects(); michael@0: } michael@0: } michael@0: michael@0: mInnerWindow = newInnerWindow; michael@0: michael@0: if (!GetWrapperPreserveColor()) { michael@0: JS::Rooted outer(cx, michael@0: NewOuterWindowProxy(cx, newInnerGlobal, thisChrome)); michael@0: NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE); michael@0: michael@0: js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this))); michael@0: michael@0: // Inform the nsJSContext, which is the canonical holder of the outer. michael@0: mContext->SetWindowProxy(outer); michael@0: mContext->DidInitializeContext(); michael@0: michael@0: SetWrapper(mContext->GetWindowProxy()); michael@0: } else { michael@0: JS::ExposeObjectToActiveJS(newInnerGlobal); michael@0: JS::Rooted outerObject(cx, michael@0: NewOuterWindowProxy(cx, newInnerGlobal, thisChrome)); michael@0: if (!outerObject) { michael@0: NS_ERROR("out of memory"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: JS::Rooted obj(cx, GetWrapperPreserveColor()); michael@0: michael@0: js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr)); michael@0: michael@0: outerObject = xpc::TransplantObject(cx, obj, outerObject); michael@0: if (!outerObject) { michael@0: NS_ERROR("unable to transplant wrappers, probably OOM"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this))); michael@0: michael@0: SetWrapper(outerObject); michael@0: michael@0: { michael@0: JSAutoCompartment ac(cx, outerObject); michael@0: michael@0: JS_SetParent(cx, outerObject, newInnerGlobal); michael@0: michael@0: // Inform the nsJSContext, which is the canonical holder of the outer. michael@0: mContext->SetWindowProxy(outerObject); michael@0: michael@0: NS_ASSERTION(!JS_IsExceptionPending(cx), michael@0: "We might overwrite a pending exception!"); michael@0: XPCWrappedNativeScope* scope = xpc::GetObjectScope(outerObject); michael@0: if (scope->mWaiverWrapperMap) { michael@0: scope->mWaiverWrapperMap->Reparent(cx, newInnerGlobal); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Enter the new global's compartment. michael@0: JSAutoCompartment ac(cx, GetWrapperPreserveColor()); michael@0: michael@0: // Set scriptability based on the state of the docshell. michael@0: bool allow = GetDocShell()->GetCanExecuteScripts(); michael@0: xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow); michael@0: michael@0: // If we created a new inner window above, we need to do the last little bit michael@0: // of initialization now that the dust has settled. michael@0: if (createdInnerWindow) { michael@0: nsIXPConnect *xpc = nsContentUtils::XPConnect(); michael@0: nsCOMPtr wrapper; michael@0: nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerGlobal, michael@0: getter_AddRefs(wrapper)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: NS_ABORT_IF_FALSE(wrapper, "bad wrapper"); michael@0: rv = wrapper->FinishInitForWrappedGlobal(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (!aState) { michael@0: JS::Rooted rootedWrapper(cx, GetWrapperPreserveColor()); michael@0: if (!JS_DefineProperty(cx, newInnerGlobal, "window", rootedWrapper, michael@0: JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT, michael@0: JS_PropertyStub, JS_StrictPropertyStub)) { michael@0: NS_ERROR("can't create the 'window' property"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: } michael@0: michael@0: JSAutoCompartment ac(cx, GetWrapperPreserveColor()); michael@0: michael@0: if (!aState && !reUseInnerWindow) { michael@0: // Loading a new page and creating a new inner window, *not* michael@0: // restoring from session history. michael@0: michael@0: // Now that both the the inner and outer windows are initialized michael@0: // let the script context do its magic to hook them together. michael@0: MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor()); michael@0: #ifdef DEBUG michael@0: JS::Rooted rootedJSObject(cx, GetWrapperPreserveColor()); michael@0: JS::Rooted proto1(cx), proto2(cx); michael@0: JS_GetPrototype(cx, rootedJSObject, &proto1); michael@0: JS_GetPrototype(cx, newInnerGlobal, &proto2); michael@0: NS_ASSERTION(proto1 == proto2, michael@0: "outer and inner globals should have the same prototype"); michael@0: #endif michael@0: michael@0: nsCOMPtr frame = GetFrameElementInternal(); michael@0: if (frame) { michael@0: nsPIDOMWindow* parentWindow = frame->OwnerDoc()->GetWindow(); michael@0: if (parentWindow && parentWindow->TimeoutSuspendCount()) { michael@0: SuspendTimeouts(parentWindow->TimeoutSuspendCount()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Add an extra ref in case we release mContext during GC. michael@0: nsCOMPtr kungFuDeathGrip(mContext); michael@0: michael@0: aDocument->SetScriptGlobalObject(newInnerWindow); michael@0: michael@0: if (!aState) { michael@0: if (reUseInnerWindow) { michael@0: if (newInnerWindow->mDoc != aDocument) { michael@0: newInnerWindow->mDoc = aDocument; michael@0: michael@0: if (newInnerWindow->IsDOMBinding()) { michael@0: WindowBinding::ClearCachedDocumentValue(cx, newInnerWindow); michael@0: } else { michael@0: // We're reusing the inner window for a new document. In this michael@0: // case we don't clear the inner window's scope, but we must michael@0: // make sure the cached document property gets updated. michael@0: michael@0: JS::Rooted obj(cx, michael@0: currentInner->GetWrapperPreserveColor()); michael@0: ::JS_DeleteProperty(cx, obj, "document"); michael@0: } michael@0: } michael@0: } else { michael@0: newInnerWindow->InnerSetNewDocument(cx, aDocument); michael@0: michael@0: // Initialize DOM classes etc on the inner window. michael@0: JS::Rooted obj(cx, newInnerGlobal); michael@0: rv = mContext->InitClasses(obj); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // If the document comes from a JAR, check if the channel was determined michael@0: // to be unsafe. If so, permanently disable script on the compartment by michael@0: // calling Block() and throwing away the key. michael@0: nsCOMPtr jarChannel = do_QueryInterface(aDocument->GetChannel()); michael@0: if (jarChannel && jarChannel->GetIsUnsafe()) { michael@0: xpc::Scriptability::Get(newInnerGlobal).Block(); michael@0: } michael@0: michael@0: if (mArguments) { michael@0: newInnerWindow->DefineArgumentsProperty(mArguments); michael@0: mArguments = nullptr; michael@0: } michael@0: michael@0: // Give the new inner window our chrome event handler (since it michael@0: // doesn't have one). michael@0: newInnerWindow->mChromeEventHandler = mChromeEventHandler; michael@0: } michael@0: michael@0: mContext->GC(JS::gcreason::SET_NEW_DOCUMENT); michael@0: mContext->DidInitializeContext(); michael@0: michael@0: // We wait to fire the debugger hook until the window is all set up and hooked michael@0: // up with the outer. See bug 969156. michael@0: if (createdInnerWindow) { michael@0: JS::Rooted global(cx, newInnerWindow->GetWrapper()); michael@0: JS_FireOnNewGlobalObject(cx, global); michael@0: } michael@0: michael@0: if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) { michael@0: // We should probably notify. However if this is the, arguably bad, michael@0: // situation when we're creating a temporary non-chrome-about-blank michael@0: // document in a chrome docshell, don't notify just yet. Instead wait michael@0: // until we have a real chrome doc. michael@0: if (!mDocShell || michael@0: mDocShell->ItemType() != nsIDocShellTreeItem::typeChrome || michael@0: nsContentUtils::IsSystemPrincipal(mDoc->NodePrincipal())) { michael@0: newInnerWindow->mHasNotifiedGlobalCreated = true; michael@0: nsContentUtils::AddScriptRunner( michael@0: NS_NewRunnableMethod(this, &nsGlobalWindow::DispatchDOMWindowCreated)); michael@0: } michael@0: } michael@0: michael@0: PreloadLocalStorage(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::PreloadLocalStorage() michael@0: { michael@0: if (!Preferences::GetBool(kStorageEnabled)) { michael@0: return; michael@0: } michael@0: michael@0: if (IsChromeWindow()) { michael@0: return; michael@0: } michael@0: michael@0: nsIPrincipal* principal = GetPrincipal(); michael@0: if (!principal) { michael@0: return; michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr firstPartyIsolationURI; michael@0: rv = GetFirstPartyIsolationURI(getter_AddRefs(firstPartyIsolationURI)); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr storageManager = michael@0: do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: storageManager->PrecacheStorageForFirstParty(firstPartyIsolationURI, principal); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::DispatchDOMWindowCreated() michael@0: { michael@0: if (!mDoc) { michael@0: return; michael@0: } michael@0: michael@0: // Fire DOMWindowCreated at chrome event listeners michael@0: nsContentUtils::DispatchChromeEvent(mDoc, mDoc, NS_LITERAL_STRING("DOMWindowCreated"), michael@0: true /* bubbles */, michael@0: false /* not cancellable */); michael@0: michael@0: nsCOMPtr observerService = michael@0: mozilla::services::GetObserverService(); michael@0: if (observerService) { michael@0: nsAutoString origin; michael@0: nsIPrincipal* principal = mDoc->NodePrincipal(); michael@0: nsContentUtils::GetUTFOrigin(principal, origin); michael@0: observerService-> michael@0: NotifyObservers(static_cast(this), michael@0: nsContentUtils::IsSystemPrincipal(principal) ? michael@0: "chrome-document-global-created" : michael@0: "content-document-global-created", michael@0: origin.get()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::ClearStatus() michael@0: { michael@0: SetStatus(EmptyString()); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::InnerSetNewDocument(JSContext* aCx, nsIDocument* aDocument) michael@0: { michael@0: NS_PRECONDITION(IsInnerWindow(), "Must only be called on inner windows"); michael@0: MOZ_ASSERT(aDocument); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (gDOMLeakPRLog && PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) { michael@0: nsIURI *uri = aDocument->GetDocumentURI(); michael@0: nsAutoCString spec; michael@0: if (uri) michael@0: uri->GetSpec(spec); michael@0: PR_LogPrint("DOMWINDOW %p SetNewDocument %s", this, spec.get()); michael@0: } michael@0: #endif michael@0: michael@0: mDoc = aDocument; michael@0: if (IsDOMBinding()) { michael@0: WindowBinding::ClearCachedDocumentValue(aCx, this); michael@0: } michael@0: mFocusedNode = nullptr; michael@0: mLocalStorage = nullptr; michael@0: mSessionStorage = nullptr; michael@0: michael@0: #ifdef DEBUG michael@0: mLastOpenedURI = aDocument->GetDocumentURI(); michael@0: #endif michael@0: michael@0: Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, michael@0: mMutationBits ? 1 : 0); michael@0: michael@0: // Clear our mutation bitfield. michael@0: mMutationBits = 0; michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell) michael@0: { michael@0: NS_ASSERTION(IsOuterWindow(), "Uh, SetDocShell() called on inner window!"); michael@0: MOZ_ASSERT(aDocShell); michael@0: michael@0: if (aDocShell == mDocShell) { michael@0: return; michael@0: } michael@0: michael@0: mDocShell = aDocShell; // Weak Reference michael@0: michael@0: NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!"); michael@0: michael@0: if (mFrames) { michael@0: mFrames->SetDocShell(aDocShell); michael@0: } michael@0: michael@0: // Get our enclosing chrome shell and retrieve its global window impl, so michael@0: // that we can do some forwarding to the chrome document. michael@0: nsCOMPtr chromeEventHandler; michael@0: mDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler)); michael@0: mChromeEventHandler = do_QueryInterface(chromeEventHandler); michael@0: if (!mChromeEventHandler) { michael@0: // We have no chrome event handler. If we have a parent, michael@0: // get our chrome event handler from the parent. If michael@0: // we don't have a parent, then we need to make a new michael@0: // window root object that will function as a chrome event michael@0: // handler and receive all events that occur anywhere inside michael@0: // our window. michael@0: nsCOMPtr parentWindow; michael@0: GetParent(getter_AddRefs(parentWindow)); michael@0: if (parentWindow.get() != static_cast(this)) { michael@0: nsCOMPtr piWindow(do_QueryInterface(parentWindow)); michael@0: mChromeEventHandler = piWindow->GetChromeEventHandler(); michael@0: } michael@0: else { michael@0: mChromeEventHandler = NS_NewWindowRoot(this); michael@0: } michael@0: } michael@0: michael@0: bool docShellActive; michael@0: mDocShell->GetIsActive(&docShellActive); michael@0: mIsBackground = !docShellActive; michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::DetachFromDocShell() michael@0: { michael@0: NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!"); michael@0: michael@0: // DetachFromDocShell means the window is being torn down. Drop our michael@0: // reference to the script context, allowing it to be deleted michael@0: // later. Meanwhile, keep our weak reference to the script object michael@0: // so that it can be retrieved later (until it is finalized by the JS GC). michael@0: michael@0: NS_ASSERTION(mTimeouts.isEmpty(), "Uh, outer window holds timeouts!"); michael@0: michael@0: // Call FreeInnerObjects on all inner windows, not just the current michael@0: // one, since some could be held by WindowStateHolder objects that michael@0: // are GC-owned. michael@0: for (nsRefPtr inner = (nsGlobalWindow *)PR_LIST_HEAD(this); michael@0: inner != this; michael@0: inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) { michael@0: NS_ASSERTION(!inner->mOuterWindow || inner->mOuterWindow == this, michael@0: "bad outer window pointer"); michael@0: inner->FreeInnerObjects(); michael@0: } michael@0: michael@0: // Make sure that this is called before we null out the document. michael@0: NotifyDOMWindowDestroyed(this); michael@0: michael@0: NotifyWindowIDDestroyed("outer-window-destroyed"); michael@0: michael@0: nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); michael@0: michael@0: if (currentInner) { michael@0: NS_ASSERTION(mDoc, "Must have doc!"); michael@0: michael@0: // Remember the document's principal and URI. michael@0: mDocumentPrincipal = mDoc->NodePrincipal(); michael@0: mDocumentURI = mDoc->GetDocumentURI(); michael@0: mDocBaseURI = mDoc->GetDocBaseURI(); michael@0: michael@0: // Release our document reference michael@0: DropOuterWindowDocs(); michael@0: mFocusedNode = nullptr; michael@0: } michael@0: michael@0: ClearControllers(); michael@0: michael@0: mChromeEventHandler = nullptr; // force release now michael@0: michael@0: if (mContext) { michael@0: mContext->GC(JS::gcreason::SET_DOC_SHELL); michael@0: mContext = nullptr; michael@0: } michael@0: michael@0: mDocShell = nullptr; // Weak Reference michael@0: michael@0: NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!"); michael@0: michael@0: if (mFrames) { michael@0: mFrames->SetDocShell(nullptr); michael@0: } michael@0: michael@0: MaybeForgiveSpamCount(); michael@0: CleanUp(); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::SetOpenerWindow(nsIDOMWindow* aOpener, michael@0: bool aOriginalOpener) michael@0: { michael@0: FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener)); michael@0: michael@0: NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled, michael@0: "aOriginalOpener is true, but not first call to " michael@0: "SetOpenerWindow!"); michael@0: NS_ASSERTION(aOpener || !aOriginalOpener, michael@0: "Shouldn't set mHadOriginalOpener if aOpener is null"); michael@0: michael@0: mOpener = do_GetWeakReference(aOpener); michael@0: NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!"); michael@0: michael@0: if (aOriginalOpener) { michael@0: mHadOriginalOpener = true; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: mSetOpenerWindowCalled = true; michael@0: #endif michael@0: } michael@0: michael@0: static michael@0: already_AddRefed michael@0: TryGetTabChildGlobalAsEventTarget(nsISupports *aFrom) michael@0: { michael@0: nsCOMPtr frameLoaderOwner = do_QueryInterface(aFrom); michael@0: if (!frameLoaderOwner) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr frameLoader = frameLoaderOwner->GetFrameLoader(); michael@0: if (!frameLoader) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr target = frameLoader->GetTabChildGlobalAsEventTarget(); michael@0: return target.forget(); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::UpdateParentTarget() michael@0: { michael@0: // Try to get our frame element's tab child global (its in-process message michael@0: // manager). If that fails, fall back to the chrome event handler's tab michael@0: // child global, and if it doesn't have one, just use the chrome event michael@0: // handler itself. michael@0: michael@0: nsCOMPtr frameElement = GetFrameElementInternal(); michael@0: nsCOMPtr eventTarget = michael@0: TryGetTabChildGlobalAsEventTarget(frameElement); michael@0: michael@0: if (!eventTarget) { michael@0: nsGlobalWindow* topWin = GetScriptableTop(); michael@0: if (topWin) { michael@0: frameElement = topWin->GetFrameElementInternal(); michael@0: eventTarget = TryGetTabChildGlobalAsEventTarget(frameElement); michael@0: } michael@0: } michael@0: michael@0: if (!eventTarget) { michael@0: eventTarget = TryGetTabChildGlobalAsEventTarget(mChromeEventHandler); michael@0: } michael@0: michael@0: if (!eventTarget) { michael@0: eventTarget = mChromeEventHandler; michael@0: } michael@0: michael@0: mParentTarget = eventTarget; michael@0: } michael@0: michael@0: EventTarget* michael@0: nsGlobalWindow::GetTargetForDOMEvent() michael@0: { michael@0: return GetOuterWindowInternal(); michael@0: } michael@0: michael@0: EventTarget* michael@0: nsGlobalWindow::GetTargetForEventTargetChain() michael@0: { michael@0: return IsInnerWindow() ? this : GetCurrentInnerWindowInternal(); michael@0: } michael@0: michael@0: nsresult michael@0: nsGlobalWindow::WillHandleEvent(EventChainPostVisitor& aVisitor) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: JSContext* michael@0: nsGlobalWindow::GetJSContextForEventHandlers() michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsGlobalWindow::PreHandleEvent(EventChainPreVisitor& aVisitor) michael@0: { michael@0: NS_PRECONDITION(IsInnerWindow(), "PreHandleEvent is used on outer window!?"); michael@0: static uint32_t count = 0; michael@0: uint32_t msg = aVisitor.mEvent->message; michael@0: michael@0: aVisitor.mCanHandle = true; michael@0: aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 michael@0: if ((msg == NS_MOUSE_MOVE) && gEntropyCollector) { michael@0: //Chances are this counter will overflow during the life of the michael@0: //process, but that's OK for our case. Means we get a little michael@0: //more entropy. michael@0: if (count++ % 100 == 0) { michael@0: //Since the high bits seem to be zero's most of the time, michael@0: //let's only take the lowest half of the point structure. michael@0: int16_t myCoord[2]; michael@0: michael@0: myCoord[0] = aVisitor.mEvent->refPoint.x; michael@0: myCoord[1] = aVisitor.mEvent->refPoint.y; michael@0: gEntropyCollector->RandomUpdate((void*)myCoord, sizeof(myCoord)); michael@0: gEntropyCollector->RandomUpdate((void*)&(aVisitor.mEvent->time), michael@0: sizeof(uint32_t)); michael@0: } michael@0: } else if (msg == NS_RESIZE_EVENT) { michael@0: mIsHandlingResizeEvent = true; michael@0: } else if (msg == NS_MOUSE_BUTTON_DOWN && michael@0: aVisitor.mEvent->mFlags.mIsTrusted) { michael@0: gMouseDown = true; michael@0: } else if ((msg == NS_MOUSE_BUTTON_UP || michael@0: msg == NS_DRAGDROP_END) && michael@0: aVisitor.mEvent->mFlags.mIsTrusted) { michael@0: gMouseDown = false; michael@0: if (gDragServiceDisabled) { michael@0: nsCOMPtr ds = michael@0: do_GetService("@mozilla.org/widget/dragservice;1"); michael@0: if (ds) { michael@0: gDragServiceDisabled = false; michael@0: ds->Unsuppress(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: aVisitor.mParentTarget = GetParentTarget(); michael@0: michael@0: // Handle 'active' event. michael@0: if (!mIdleObservers.IsEmpty() && michael@0: aVisitor.mEvent->mFlags.mIsTrusted && michael@0: (aVisitor.mEvent->HasMouseEventMessage() || michael@0: aVisitor.mEvent->HasDragEventMessage())) { michael@0: mAddActiveEventFuzzTime = false; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsGlobalWindow::ShouldPromptToBlockDialogs() michael@0: { michael@0: MOZ_ASSERT(IsOuterWindow()); michael@0: michael@0: nsGlobalWindow *topWindow = GetScriptableTop(); michael@0: if (!topWindow) { michael@0: NS_ASSERTION(!mDocShell, "ShouldPromptToBlockDialogs() called without a top window?"); michael@0: return true; michael@0: } michael@0: michael@0: topWindow = topWindow->GetCurrentInnerWindowInternal(); michael@0: if (!topWindow) { michael@0: return true; michael@0: } michael@0: michael@0: return topWindow->DialogsAreBeingAbused(); michael@0: } michael@0: michael@0: bool michael@0: nsGlobalWindow::AreDialogsEnabled() michael@0: { michael@0: nsGlobalWindow *topWindow = GetScriptableTop(); michael@0: if (!topWindow) { michael@0: NS_ERROR("AreDialogsEnabled() called without a top window?"); michael@0: return false; michael@0: } michael@0: michael@0: // TODO: Warn if no top window? michael@0: topWindow = topWindow->GetCurrentInnerWindowInternal(); michael@0: if (!topWindow) { michael@0: return false; michael@0: } michael@0: michael@0: // Dialogs are blocked if the content viewer is hidden michael@0: if (mDocShell) { michael@0: nsCOMPtr cv; michael@0: mDocShell->GetContentViewer(getter_AddRefs(cv)); michael@0: michael@0: bool isHidden; michael@0: cv->GetIsHidden(&isHidden); michael@0: if (isHidden) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return topWindow->mAreDialogsEnabled; michael@0: } michael@0: michael@0: bool michael@0: nsGlobalWindow::DialogsAreBeingAbused() michael@0: { michael@0: MOZ_ASSERT(IsInnerWindow()); michael@0: NS_ASSERTION(GetScriptableTop() && michael@0: GetScriptableTop()->GetCurrentInnerWindowInternal() == this, michael@0: "DialogsAreBeingAbused called with invalid window"); michael@0: michael@0: if (mLastDialogQuitTime.IsNull() || michael@0: nsContentUtils::IsCallerChrome()) { michael@0: return false; michael@0: } michael@0: michael@0: TimeDuration dialogInterval(TimeStamp::Now() - mLastDialogQuitTime); michael@0: if (dialogInterval.ToSeconds() < michael@0: Preferences::GetInt("dom.successive_dialog_time_limit", michael@0: DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT)) { michael@0: mDialogAbuseCount++; michael@0: michael@0: return GetPopupControlState() > openAllowed || michael@0: mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT; michael@0: } michael@0: michael@0: // Reset the abuse counter michael@0: mDialogAbuseCount = 0; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsGlobalWindow::ConfirmDialogIfNeeded() michael@0: { michael@0: MOZ_ASSERT(IsOuterWindow()); michael@0: michael@0: NS_ENSURE_TRUE(mDocShell, false); michael@0: nsCOMPtr promptSvc = michael@0: do_GetService("@mozilla.org/embedcomp/prompt-service;1"); michael@0: michael@0: if (!promptSvc) { michael@0: return true; michael@0: } michael@0: michael@0: // Reset popup state while opening a modal dialog, and firing events michael@0: // about the dialog, to prevent the current state from being active michael@0: // the whole time a modal dialog is open. michael@0: nsAutoPopupStatePusher popupStatePusher(openAbused, true); michael@0: michael@0: bool disableDialog = false; michael@0: nsXPIDLString label, title; michael@0: nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, michael@0: "ScriptDialogLabel", label); michael@0: nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, michael@0: "ScriptDialogPreventTitle", title); michael@0: promptSvc->Confirm(this, title.get(), label.get(), &disableDialog); michael@0: if (disableDialog) { michael@0: DisableDialogs(); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::DisableDialogs() michael@0: { michael@0: nsGlobalWindow *topWindow = GetScriptableTop(); michael@0: if (!topWindow) { michael@0: NS_ERROR("DisableDialogs() called without a top window?"); michael@0: return; michael@0: } michael@0: michael@0: topWindow = topWindow->GetCurrentInnerWindowInternal(); michael@0: // TODO: Warn if no top window? michael@0: if (topWindow) { michael@0: topWindow->mAreDialogsEnabled = false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::EnableDialogs() michael@0: { michael@0: nsGlobalWindow *topWindow = GetScriptableTop(); michael@0: if (!topWindow) { michael@0: NS_ERROR("EnableDialogs() called without a top window?"); michael@0: return; michael@0: } michael@0: michael@0: // TODO: Warn if no top window? michael@0: topWindow = topWindow->GetCurrentInnerWindowInternal(); michael@0: if (topWindow) { michael@0: topWindow->mAreDialogsEnabled = true; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsGlobalWindow::PostHandleEvent(EventChainPostVisitor& aVisitor) michael@0: { michael@0: NS_PRECONDITION(IsInnerWindow(), "PostHandleEvent is used on outer window!?"); michael@0: michael@0: // Return early if there is nothing to do. michael@0: switch (aVisitor.mEvent->message) { michael@0: case NS_RESIZE_EVENT: michael@0: case NS_PAGE_UNLOAD: michael@0: case NS_LOAD: michael@0: break; michael@0: default: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* mChromeEventHandler and mContext go dangling in the middle of this michael@0: function under some circumstances (events that destroy the window) michael@0: without this addref. */ michael@0: nsCOMPtr kungFuDeathGrip1(mChromeEventHandler); michael@0: nsCOMPtr kungFuDeathGrip2(GetContextInternal()); michael@0: michael@0: if (aVisitor.mEvent->message == NS_RESIZE_EVENT) { michael@0: mIsHandlingResizeEvent = false; michael@0: } else if (aVisitor.mEvent->message == NS_PAGE_UNLOAD && michael@0: aVisitor.mEvent->mFlags.mIsTrusted) { michael@0: // Execute bindingdetached handlers before we tear ourselves michael@0: // down. michael@0: if (mDoc) { michael@0: mDoc->BindingManager()->ExecuteDetachedHandlers(); michael@0: } michael@0: mIsDocumentLoaded = false; michael@0: } else if (aVisitor.mEvent->message == NS_LOAD && michael@0: aVisitor.mEvent->mFlags.mIsTrusted) { michael@0: // This is page load event since load events don't propagate to |window|. michael@0: // @see nsDocument::PreHandleEvent. michael@0: mIsDocumentLoaded = true; michael@0: michael@0: nsCOMPtr element = GetFrameElementInternal(); michael@0: nsIDocShell* docShell = GetDocShell(); michael@0: if (element && GetParentInternal() && michael@0: docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) { michael@0: // If we're not in chrome, or at a chrome boundary, fire the michael@0: // onload event for the frame element. michael@0: michael@0: nsEventStatus status = nsEventStatus_eIgnore; michael@0: WidgetEvent event(aVisitor.mEvent->mFlags.mIsTrusted, NS_LOAD); michael@0: event.mFlags.mBubbles = false; michael@0: michael@0: // Most of the time we could get a pres context to pass in here, michael@0: // but not always (i.e. if this window is not shown there won't michael@0: // be a pres context available). Since we're not firing a GUI michael@0: // event we don't need a pres context anyway so we just pass michael@0: // null as the pres context all the time here. michael@0: EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGlobalWindow::DispatchDOMEvent(WidgetEvent* aEvent, michael@0: nsIDOMEvent* aDOMEvent, michael@0: nsPresContext* aPresContext, michael@0: nsEventStatus* aEventStatus) michael@0: { michael@0: return EventDispatcher::DispatchDOMEvent(static_cast(this), michael@0: aEvent, aDOMEvent, aPresContext, michael@0: aEventStatus); michael@0: } michael@0: michael@0: void michael@0: nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject) michael@0: { michael@0: MOZ_ASSERT(IsOuterWindow()); michael@0: if (aObject == GetWrapperPreserveColor()) { michael@0: PoisonWrapper(); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsGlobalWindow::SetArguments(nsIArray *aArguments) michael@0: { michael@0: MOZ_ASSERT(IsOuterWindow()); michael@0: nsresult rv; michael@0: michael@0: // Historically, we've used the same machinery to handle openDialog arguments michael@0: // (exposed via window.arguments) and showModalDialog arguments (exposed via michael@0: // window.dialogArguments), even though the former is XUL-only and uses an XPCOM michael@0: // array while the latter is web-exposed and uses an arbitrary JS value. michael@0: // Moreover, per-spec |dialogArguments| is a property of the browsing context michael@0: // (outer), whereas |arguments| lives on the inner. michael@0: // michael@0: // We've now mostly separated them, but the difference is still opaque to michael@0: // nsWindowWatcher (the caller of SetArguments in this little back-and-forth michael@0: // embedding waltz we do here). michael@0: // michael@0: // So we need to demultiplex the two cases here. michael@0: nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); michael@0: if (mIsModalContentWindow) { michael@0: // nsWindowWatcher blindly converts the original nsISupports into an array michael@0: // of length 1. We need to recover it, and then cast it back to the concrete michael@0: // object we know it to be. michael@0: nsCOMPtr supports = do_QueryElementAt(aArguments, 0, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mDialogArguments = static_cast(supports.get()); michael@0: } else { michael@0: mArguments = aArguments; michael@0: rv = currentInner->DefineArgumentsProperty(aArguments); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments) michael@0: { michael@0: MOZ_ASSERT(!mIsModalContentWindow); // Handled separately. michael@0: nsIScriptContext *ctx = GetOuterWindowInternal()->mContext; michael@0: NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED); michael@0: AutoJSContext cx; michael@0: michael@0: JS::Rooted obj(cx, GetWrapperPreserveColor()); michael@0: return ctx->SetProperty(obj, "arguments", aArguments); michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // nsGlobalWindow::nsIScriptObjectPrincipal michael@0: //***************************************************************************** michael@0: michael@0: nsIPrincipal* michael@0: nsGlobalWindow::GetPrincipal() michael@0: { michael@0: if (mDoc) { michael@0: // If we have a document, get the principal from the document michael@0: return mDoc->NodePrincipal(); michael@0: } michael@0: michael@0: if (mDocumentPrincipal) { michael@0: return mDocumentPrincipal; michael@0: } michael@0: michael@0: // If we don't have a principal and we don't have a document we michael@0: // ask the parent window for the principal. This can happen when michael@0: // loading a frameset that has a , in michael@0: // that case the global window is used in JS before we've loaded michael@0: // a document into the window. michael@0: michael@0: nsCOMPtr objPrincipal = michael@0: do_QueryInterface(GetParentInternal()); michael@0: michael@0: if (objPrincipal) { michael@0: return objPrincipal->GetPrincipal(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: //***************************************************************************** michael@0: // nsGlobalWindow::nsIDOMWindow michael@0: //***************************************************************************** michael@0: michael@0: nsIURI* michael@0: nsPIDOMWindow::GetDocumentURI() const michael@0: { michael@0: return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get(); michael@0: } michael@0: michael@0: nsIURI* michael@0: nsPIDOMWindow::GetDocBaseURI() const michael@0: { michael@0: return mDoc ? mDoc->GetDocBaseURI() : mDocBaseURI.get(); michael@0: } michael@0: michael@0: void michael@0: nsPIDOMWindow::MaybeCreateDoc() michael@0: { michael@0: MOZ_ASSERT(!mDoc); michael@0: if (nsIDocShell* docShell = GetDocShell()) { michael@0: // Note that |document| here is the same thing as our mDoc, but we michael@0: // don't have to explicitly set the member variable because the docshell michael@0: // has already called SetNewDocument(). michael@0: nsCOMPtr document = do_GetInterface(docShell); michael@0: } michael@0: } michael@0: michael@0: Element* michael@0: nsPIDOMWindow::GetFrameElementInternal() const michael@0: { michael@0: if (mOuterWindow) { michael@0: return mOuterWindow->GetFrameElementInternal(); michael@0: } michael@0: michael@0: NS_ASSERTION(!IsInnerWindow(), michael@0: "GetFrameElementInternal() called on orphan inner window"); michael@0: michael@0: return mFrameElement; michael@0: } michael@0: michael@0: void michael@0: nsPIDOMWindow::SetFrameElementInternal(Element* aFrameElement) michael@0: { michael@0: if (IsOuterWindow()) { michael@0: mFrameElement = aFrameElement; michael@0: michael@0: return; michael@0: } michael@0: michael@0: if (!mOuterWindow) { michael@0: NS_ERROR("frameElement set on inner window with no outer!"); michael@0: michael@0: return; michael@0: } michael@0: michael@0: mOuterWindow->SetFrameElementInternal(aFrameElement); michael@0: } michael@0: michael@0: void michael@0: nsPIDOMWindow::AddAudioContext(AudioContext* aAudioContext) michael@0: { michael@0: mAudioContexts.AppendElement(aAudioContext); michael@0: michael@0: nsIDocShell* docShell = GetDocShell(); michael@0: if (docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline()) { michael@0: aAudioContext->Mute(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPIDOMWindow::RemoveAudioContext(AudioContext* aAudioContext) michael@0: { michael@0: mAudioContexts.RemoveElement(aAudioContext); michael@0: } michael@0: michael@0: void michael@0: nsPIDOMWindow::MuteAudioContexts() michael@0: { michael@0: for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { michael@0: if (!mAudioContexts[i]->IsOffline()) { michael@0: mAudioContexts[i]->Mute(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPIDOMWindow::UnmuteAudioContexts() michael@0: { michael@0: for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { michael@0: if (!mAudioContexts[i]->IsOffline()) { michael@0: mAudioContexts[i]->Unmute(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGlobalWindow::GetDocument(nsIDOMDocument** aDocument) michael@0: { michael@0: nsCOMPtr document = do_QueryInterface(GetDocument()); michael@0: document.forget(aDocument); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIDOMWindow* michael@0: nsGlobalWindow::GetWindow(ErrorResult& aError) michael@0: { michael@0: FORWARD_TO_OUTER_OR_THROW(GetWindow, (aError), aError, nullptr); michael@0: michael@0: return this; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGlobalWindow::GetWindow(nsIDOMWindow** aWindow) michael@0: { michael@0: ErrorResult rv; michael@0: nsCOMPtr window = GetWindow(rv); michael@0: window.forget(aWindow); michael@0: michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: nsIDOMWindow* michael@0: nsGlobalWindow::GetSelf(ErrorResult& aError) michael@0: { michael@0: FORWARD_TO_OUTER_OR_THROW(GetSelf, (aError), aError, nullptr); michael@0: michael@0: return this; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGlobalWindow::GetSelf(nsIDOMWindow** aWindow) michael@0: { michael@0: ErrorResult rv; michael@0: nsCOMPtr window = GetSelf(rv); michael@0: window.forget(aWindow); michael@0: michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: Navigator* michael@0: nsGlobalWindow::GetNavigator(ErrorResult& aError) michael@0: { michael@0: FORWARD_TO_INNER_OR_THROW(GetNavigator, (aError), aError, nullptr); michael@0: michael@0: if (!mNavigator) { michael@0: mNavigator = new Navigator(this); michael@0: } michael@0: michael@0: return mNavigator; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGlobalWindow::GetNavigator(nsIDOMNavigator** aNavigator) michael@0: { michael@0: ErrorResult rv; michael@0: nsCOMPtr navigator = GetNavigator(rv); michael@0: navigator.forget(aNavigator); michael@0: michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: nsScreen* michael@0: nsGlobalWindow::GetScreen(ErrorResult& aError) michael@0: { michael@0: FORWARD_TO_INNER_OR_THROW(GetScreen, (aError), aError, nullptr); michael@0: michael@0: if (!mScreen) { michael@0: mScreen = nsScreen::Create(this); michael@0: if (!mScreen) { michael@0: aError.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: return mScreen; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGlobalWindow::GetScreen(nsIDOMScreen** aScreen) michael@0: { michael@0: ErrorResult rv; michael@0: nsRefPtr screen = GetScreen(rv); michael@0: screen.forget(aScreen); michael@0: michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: nsHistory* michael@0: nsGlobalWindow::GetHistory(ErrorResult& aError) michael@0: { michael@0: FORWARD_TO_INNER_OR_THROW(GetHistory, (aError), aError, nullptr); michael@0: michael@0: if (!mHistory) { michael@0: mHistory = new nsHistory(this); michael@0: } michael@0: michael@0: return mHistory; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGlobalWindow::GetHistory(nsISupports** aHistory) michael@0: { michael@0: ErrorResult rv; michael@0: nsCOMPtr history = GetHistory(rv); michael@0: history.forget(aHistory); michael@0: michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: nsPerformance* michael@0: nsGlobalWindow::GetPerformance(ErrorResult& aError) michael@0: { michael@0: FORWARD_TO_INNER_OR_THROW(GetPerformance, (aError), aError, nullptr); michael@0: michael@0: nsPerformance* p = nsPIDOMWindow::GetPerformance(); michael@0: if (!p) { michael@0: aError.Throw(NS_ERROR_FAILURE); michael@0: } michael@0: return p; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGlobalWindow::GetPerformance(nsISupports** aPerformance) michael@0: { michael@0: ErrorResult rv; michael@0: nsCOMPtr performance = GetPerformance(rv); michael@0: performance.forget(aPerformance); michael@0: michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: nsPerformance* michael@0: nsPIDOMWindow::GetPerformance() michael@0: { michael@0: MOZ_ASSERT(IsInnerWindow()); michael@0: CreatePerformanceObjectIfNeeded(); michael@0: return mPerformance; michael@0: } michael@0: michael@0: void michael@0: nsPIDOMWindow::CreatePerformanceObjectIfNeeded() michael@0: { michael@0: if (mPerformance || !mDoc) { michael@0: return; michael@0: } michael@0: nsRefPtr timing = mDoc->GetNavigationTiming(); michael@0: nsCOMPtr timedChannel(do_QueryInterface(mDoc->GetChannel())); michael@0: bool timingEnabled = false; michael@0: if (!timedChannel || michael@0: !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) || michael@0: !timingEnabled) { michael@0: timedChannel = nullptr; michael@0: } michael@0: if (timing) { michael@0: // If we are dealing with an iframe, we will need the parent's performance michael@0: // object (so we can add the iframe as a resource of that page). michael@0: nsPerformance* parentPerformance = nullptr; michael@0: nsCOMPtr parentWindow; michael@0: GetScriptableParent(getter_AddRefs(parentWindow)); michael@0: nsCOMPtr parentPWindow = do_GetInterface(parentWindow); michael@0: if (GetOuterWindow() != parentPWindow) { michael@0: if (parentPWindow && !parentPWindow->IsInnerWindow()) { michael@0: parentPWindow = parentPWindow->GetCurrentInnerWindow(); michael@0: } michael@0: if (parentPWindow) { michael@0: parentPerformance = parentPWindow->GetPerformance(); michael@0: } michael@0: } michael@0: mPerformance = michael@0: new nsPerformance(this, timing, timedChannel, parentPerformance); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsPIDOMWindow::GetAudioMuted() const michael@0: { michael@0: if (!IsInnerWindow()) { michael@0: return mInnerWindow->GetAudioMuted(); michael@0: } michael@0: michael@0: return mAudioMuted; michael@0: } michael@0: michael@0: void michael@0: nsPIDOMWindow::SetAudioMuted(bool aMuted) michael@0: { michael@0: if (!IsInnerWindow()) { michael@0: mInnerWindow->SetAudioMuted(aMuted); michael@0: return; michael@0: } michael@0: michael@0: if (mAudioMuted == aMuted) { michael@0: return; michael@0: } michael@0: michael@0: mAudioMuted = aMuted; michael@0: RefreshMediaElements(); michael@0: } michael@0: michael@0: float michael@0: nsPIDOMWindow::GetAudioVolume() const michael@0: { michael@0: if (!IsInnerWindow()) { michael@0: return mInnerWindow->GetAudioVolume(); michael@0: } michael@0: michael@0: return mAudioVolume; michael@0: } michael@0: michael@0: nsresult michael@0: nsPIDOMWindow::SetAudioVolume(float aVolume) michael@0: { michael@0: if (!IsInnerWindow()) { michael@0: return mInnerWindow->SetAudioVolume(aVolume); michael@0: } michael@0: michael@0: if (aVolume < 0.0) { michael@0: return NS_ERROR_DOM_INDEX_SIZE_ERR; michael@0: } michael@0: michael@0: if (mAudioVolume == aVolume) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: mAudioVolume = aVolume; michael@0: RefreshMediaElements(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: float michael@0: nsPIDOMWindow::GetAudioGlobalVolume() michael@0: { michael@0: float globalVolume = 1.0; michael@0: nsCOMPtr window = this; michael@0: michael@0: do { michael@0: if (window->GetAudioMuted()) { michael@0: return 0; michael@0: } michael@0: michael@0: globalVolume *= window->GetAudioVolume(); michael@0: michael@0: nsCOMPtr win; michael@0: window->GetParent(getter_AddRefs(win)); michael@0: if (window == win) { michael@0: break; michael@0: } michael@0: michael@0: window = do_QueryInterface(win); michael@0: michael@0: // If there is not parent, or we are the toplevel or the volume is michael@0: // already 0.0, we don't continue. michael@0: } while (window && window != this && globalVolume); michael@0: michael@0: return globalVolume; michael@0: } michael@0: michael@0: void michael@0: nsPIDOMWindow::RefreshMediaElements() michael@0: { michael@0: nsRefPtr service = michael@0: AudioChannelService::GetAudioChannelService(); michael@0: if (service) { michael@0: service->RefreshAgentsVolume(this); michael@0: } michael@0: } michael@0: michael@0: // nsISpeechSynthesisGetter michael@0: michael@0: #ifdef MOZ_WEBSPEECH michael@0: SpeechSynthesis* michael@0: nsGlobalWindow::GetSpeechSynthesis(ErrorResult& aError) michael@0: { michael@0: FORWARD_TO_INNER_OR_THROW(GetSpeechSynthesis, (aError), aError, nullptr); michael@0: michael@0: if (!mSpeechSynthesis) { michael@0: mSpeechSynthesis = new SpeechSynthesis(this); michael@0: } michael@0: michael@0: return mSpeechSynthesis; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGlobalWindow::GetSpeechSynthesis(nsISupports** aSpeechSynthesis) michael@0: { michael@0: ErrorResult rv; michael@0: nsCOMPtr speechSynthesis; michael@0: if (Preferences::GetBool("media.webspeech.synth.enabled")) { michael@0: speechSynthesis = GetSpeechSynthesis(rv); michael@0: } michael@0: speechSynthesis.forget(aSpeechSynthesis); michael@0: michael@0: return rv.ErrorCode(); michael@0: } michael@0: #endif michael@0: michael@0: already_AddRefed michael@0: nsGlobalWindow::GetParent(ErrorResult& aError) michael@0: { michael@0: FORWARD_TO_OUTER_OR_THROW(GetParent, (aError), aError, nullptr); michael@0: michael@0: if (!mDocShell) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr parent; michael@0: if (mDocShell->GetIsBrowserOrApp()) { michael@0: parent = this; michael@0: } else { michael@0: aError = GetRealParent(getter_AddRefs(parent)); michael@0: } michael@0: michael@0: return parent.forget(); michael@0: } michael@0: michael@0: /** michael@0: * GetScriptableParent is called when script reads window.parent. michael@0: * michael@0: * In contrast to GetRealParent, GetScriptableParent respects