Mirror of roytam1's Pale Moon fork just in case Moonchild and Tobin decide to go after him
https://github.com/roytam1/palemoon27
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
14453 lines
415 KiB
14453 lines
415 KiB
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
|
/* This Source Code Form is subject to the terms of the Mozilla Public |
|
* License, v. 2.0. If a copy of the MPL was not distributed with this |
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
|
|
#include "nsGlobalWindow.h" |
|
|
|
#include <algorithm> |
|
|
|
#include "mozilla/MemoryReporting.h" |
|
|
|
// Local Includes |
|
#include "Navigator.h" |
|
#include "nsScreen.h" |
|
#include "nsHistory.h" |
|
#include "nsPerformance.h" |
|
#include "nsDOMNavigationTiming.h" |
|
#include "nsIDOMStorageManager.h" |
|
#include "mozilla/dom/DOMStorage.h" |
|
#include "mozilla/dom/StorageEvent.h" |
|
#include "mozilla/dom/StorageEventBinding.h" |
|
#include "nsDOMOfflineResourceList.h" |
|
#include "nsError.h" |
|
#include "nsIIdleService.h" |
|
#include "nsISizeOfEventTarget.h" |
|
#include "nsDOMJSUtils.h" |
|
#include "nsArrayUtils.h" |
|
#include "nsIDOMWindowCollection.h" |
|
#include "nsDOMWindowList.h" |
|
#include "mozilla/dom/WakeLock.h" |
|
#include "mozilla/dom/power/PowerManagerService.h" |
|
#include "nsIDocShellTreeOwner.h" |
|
#include "nsIInterfaceRequestorUtils.h" |
|
#include "nsIPermissionManager.h" |
|
#include "nsIScriptContext.h" |
|
#include "nsIScriptTimeoutHandler.h" |
|
#include "nsIController.h" |
|
#include "nsScriptNameSpaceManager.h" |
|
#include "nsISlowScriptDebug.h" |
|
#include "nsWindowMemoryReporter.h" |
|
#include "WindowNamedPropertiesHandler.h" |
|
#include "nsFrameSelection.h" |
|
#include "nsNetUtil.h" |
|
|
|
// Helper Classes |
|
#include "nsJSUtils.h" |
|
#include "jsapi.h" // for JSAutoRequest |
|
#include "jswrapper.h" |
|
#include "nsCharSeparatedTokenizer.h" |
|
#include "nsReadableUtils.h" |
|
#include "nsDOMClassInfo.h" |
|
#include "nsJSEnvironment.h" |
|
#include "ScriptSettings.h" |
|
#include "mozilla/Preferences.h" |
|
#include "mozilla/Likely.h" |
|
#include "mozilla/unused.h" |
|
#include "../../js/public/HashTable.h" // for MultiCompartmentMatcher |
|
|
|
// Other Classes |
|
#include "mozilla/dom/BarProps.h" |
|
#include "nsContentCID.h" |
|
#include "nsLayoutStatics.h" |
|
#include "nsCCUncollectableMarker.h" |
|
#include "mozilla/dom/workers/Workers.h" |
|
#include "mozilla/dom/ToJSValue.h" |
|
#include "nsJSPrincipals.h" |
|
#include "mozilla/Attributes.h" |
|
#include "mozilla/Debug.h" |
|
#include "mozilla/EventListenerManager.h" |
|
#include "mozilla/EventStates.h" |
|
#include "mozilla/MouseEvents.h" |
|
#include "mozilla/ProcessHangMonitor.h" |
|
#include "AudioChannelService.h" |
|
#include "nsAboutProtocolUtils.h" |
|
#include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE |
|
#include "PostMessageEvent.h" |
|
|
|
// Interfaces Needed |
|
#include "nsIFrame.h" |
|
#include "nsCanvasFrame.h" |
|
#include "nsIWidget.h" |
|
#include "nsIWidgetListener.h" |
|
#include "nsIBaseWindow.h" |
|
#include "nsIDeviceSensors.h" |
|
#include "nsIContent.h" |
|
#include "nsIDocShell.h" |
|
#include "nsIDocCharset.h" |
|
#include "nsIDocument.h" |
|
#include "Crypto.h" |
|
#include "nsIDOMDocument.h" |
|
#include "nsIDOMElement.h" |
|
#include "nsIDOMEvent.h" |
|
#include "nsIDOMOfflineResourceList.h" |
|
#include "nsDOMString.h" |
|
#include "nsIEmbeddingSiteWindow.h" |
|
#include "nsThreadUtils.h" |
|
#include "nsILoadContext.h" |
|
#include "nsIPresShell.h" |
|
#include "nsIScrollableFrame.h" |
|
#include "nsView.h" |
|
#include "nsViewManager.h" |
|
#include "nsISelectionController.h" |
|
#include "nsISelection.h" |
|
#include "nsIPrompt.h" |
|
#include "nsIPromptService.h" |
|
#include "nsIPromptFactory.h" |
|
#include "nsIWritablePropertyBag2.h" |
|
#include "nsIWebNavigation.h" |
|
#include "nsIWebBrowserChrome.h" |
|
#include "nsIWebBrowserFind.h" // For window.find() |
|
#include "nsIWindowMediator.h" // For window.find() |
|
#include "nsComputedDOMStyle.h" |
|
#include "nsIEntropyCollector.h" |
|
#include "nsDOMCID.h" |
|
#include "nsDOMWindowUtils.h" |
|
#include "nsIWindowWatcher.h" |
|
#include "nsPIWindowWatcher.h" |
|
#include "nsIContentViewer.h" |
|
#include "nsIScriptError.h" |
|
#include "nsIControllers.h" |
|
#include "nsIControllerContext.h" |
|
#include "nsGlobalWindowCommands.h" |
|
#include "nsAutoPtr.h" |
|
#include "nsQueryObject.h" |
|
#include "nsContentUtils.h" |
|
#include "nsCSSProps.h" |
|
#include "nsIDOMFileList.h" |
|
#include "nsIURIFixup.h" |
|
#ifndef DEBUG |
|
#include "nsIAppStartup.h" |
|
#include "nsToolkitCompsCID.h" |
|
#endif |
|
#include "nsCDefaultURIFixup.h" |
|
#include "mozilla/EventDispatcher.h" |
|
#include "mozilla/EventStateManager.h" |
|
#include "nsIObserverService.h" |
|
#include "nsFocusManager.h" |
|
#include "nsIXULWindow.h" |
|
#include "nsITimedChannel.h" |
|
#include "nsServiceManagerUtils.h" |
|
#ifdef MOZ_XUL |
|
#include "nsIDOMXULControlElement.h" |
|
#include "nsMenuPopupFrame.h" |
|
#endif |
|
#include "mozilla/dom/CustomEvent.h" |
|
#include "nsIJARChannel.h" |
|
|
|
#include "xpcprivate.h" |
|
|
|
#ifdef NS_PRINTING |
|
#include "nsIPrintSettings.h" |
|
#include "nsIPrintSettingsService.h" |
|
#include "nsIWebBrowserPrint.h" |
|
#endif |
|
|
|
#include "nsWindowRoot.h" |
|
#include "nsNetCID.h" |
|
#include "nsIArray.h" |
|
|
|
// XXX An unfortunate dependency exists here (two XUL files). |
|
#include "nsIDOMXULDocument.h" |
|
#include "nsIDOMXULCommandDispatcher.h" |
|
|
|
#include "nsBindingManager.h" |
|
#include "nsXBLService.h" |
|
|
|
// used for popup blocking, needs to be converted to something |
|
// belonging to the back-end like nsIContentPolicy |
|
#include "nsIPopupWindowManager.h" |
|
|
|
#include "nsIDragService.h" |
|
#include "mozilla/dom/Element.h" |
|
#include "mozilla/dom/Selection.h" |
|
#include "nsFrameLoader.h" |
|
#include "nsISupportsPrimitives.h" |
|
#include "nsXPCOMCID.h" |
|
#include "mozilla/Logging.h" |
|
#include "prenv.h" |
|
#include "prprf.h" |
|
|
|
#include "mozilla/dom/MessageChannel.h" |
|
#include "mozilla/dom/indexedDB/IDBFactory.h" |
|
#include "mozilla/dom/Promise.h" |
|
|
|
#ifdef MOZ_GAMEPAD |
|
#include "mozilla/dom/Gamepad.h" |
|
#include "mozilla/dom/GamepadService.h" |
|
#endif |
|
|
|
#include "mozilla/dom/VRDevice.h" |
|
|
|
#include "nsRefreshDriver.h" |
|
|
|
#include "mozilla/AddonPathService.h" |
|
#include "mozilla/BasePrincipal.h" |
|
#include "mozilla/Services.h" |
|
#include "mozilla/Telemetry.h" |
|
#include "nsLocation.h" |
|
#include "nsHTMLDocument.h" |
|
#include "nsWrapperCacheInlines.h" |
|
#include "mozilla/DOMEventTargetHelper.h" |
|
#include "prrng.h" |
|
#include "nsSandboxFlags.h" |
|
#include "TimeChangeObserver.h" |
|
#include "TouchCaret.h" |
|
#include "mozilla/dom/AudioContext.h" |
|
#include "mozilla/dom/BrowserElementDictionariesBinding.h" |
|
#include "mozilla/dom/cache/CacheStorage.h" |
|
#include "mozilla/dom/Console.h" |
|
#include "mozilla/dom/Fetch.h" |
|
#include "mozilla/dom/FunctionBinding.h" |
|
#include "mozilla/dom/HashChangeEvent.h" |
|
#include "mozilla/dom/MozSelfSupportBinding.h" |
|
#include "mozilla/dom/PopStateEvent.h" |
|
#include "mozilla/dom/PopupBlockedEvent.h" |
|
#include "mozilla/dom/PrimitiveConversions.h" |
|
#include "mozilla/dom/WindowBinding.h" |
|
#include "nsITabChild.h" |
|
#include "mozilla/dom/MediaQueryList.h" |
|
#include "mozilla/dom/ScriptSettings.h" |
|
#include "mozilla/dom/NavigatorBinding.h" |
|
#include "mozilla/dom/ImageBitmap.h" |
|
#ifdef HAVE_SIDEBAR |
|
#include "mozilla/dom/ExternalBinding.h" |
|
#endif |
|
|
|
#ifdef MOZ_WEBSPEECH |
|
#include "mozilla/dom/SpeechSynthesis.h" |
|
#endif |
|
|
|
#ifdef MOZ_B2G |
|
#include "nsPISocketTransportService.h" |
|
#endif |
|
|
|
// Apple system headers seem to have a check() macro. <sigh> |
|
#ifdef check |
|
class nsIScriptTimeoutHandler; |
|
#undef check |
|
#endif // check |
|
#include "AccessCheck.h" |
|
|
|
#ifdef ANDROID |
|
#include <android/log.h> |
|
#endif |
|
|
|
static PRLogModuleInfo* gDOMLeakPRLog; |
|
|
|
#ifdef XP_WIN |
|
#include <process.h> |
|
#define getpid _getpid |
|
#else |
|
#include <unistd.h> // for getpid() |
|
#endif |
|
|
|
static const char kStorageEnabled[] = "dom.storage.enabled"; |
|
|
|
using namespace mozilla; |
|
using namespace mozilla::dom; |
|
using namespace mozilla::dom::ipc; |
|
using mozilla::BasePrincipal; |
|
using mozilla::OriginAttributes; |
|
using mozilla::TimeStamp; |
|
using mozilla::TimeDuration; |
|
using mozilla::dom::cache::CacheStorage; |
|
using mozilla::dom::indexedDB::IDBFactory; |
|
|
|
nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr; |
|
bool nsGlobalWindow::sWarnedAboutWindowInternal = false; |
|
bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false; |
|
|
|
static nsIEntropyCollector *gEntropyCollector = nullptr; |
|
static int32_t gRefCnt = 0; |
|
static int32_t gOpenPopupSpamCount = 0; |
|
static PopupControlState gPopupControlState = openAbused; |
|
static int32_t gRunningTimeoutDepth = 0; |
|
static bool gMouseDown = false; |
|
static bool gDragServiceDisabled = false; |
|
static FILE *gDumpFile = nullptr; |
|
static uint32_t gSerialCounter = 0; |
|
static uint32_t gTimeoutsRecentlySet = 0; |
|
static TimeStamp gLastRecordedRecentTimeouts; |
|
#define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC) |
|
|
|
#ifdef DEBUG_jst |
|
int32_t gTimeoutCnt = 0; |
|
#endif |
|
|
|
#if defined(DEBUG_bryner) || defined(DEBUG_chb) |
|
#define DEBUG_PAGE_CACHE |
|
#endif |
|
|
|
#define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added" |
|
|
|
// The default shortest interval/timeout we permit |
|
#define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms |
|
#define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms |
|
static int32_t gMinTimeoutValue; |
|
static int32_t gMinBackgroundTimeoutValue; |
|
inline int32_t |
|
nsGlobalWindow::DOMMinTimeoutValue() const { |
|
bool isBackground = !mOuterWindow || mOuterWindow->IsBackground(); |
|
return |
|
std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, 0); |
|
} |
|
|
|
// The number of nested timeouts before we start clamping. HTML5 says 1, WebKit |
|
// uses 5. |
|
#define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5 |
|
|
|
// The longest interval (as PRIntervalTime) we permit, or that our |
|
// timer code can handle, really. See DELAY_INTERVAL_LIMIT in |
|
// nsTimerImpl.h for details. |
|
#define DOM_MAX_TIMEOUT_VALUE DELAY_INTERVAL_LIMIT |
|
|
|
#define FORWARD_TO_OUTER(method, args, err_rval) \ |
|
PR_BEGIN_MACRO \ |
|
if (IsInnerWindow()) { \ |
|
nsGlobalWindow *outer = GetOuterWindowInternal(); \ |
|
if (!HasActiveDocument()) { \ |
|
NS_WARNING(outer ? \ |
|
"Inner window does not have active document." : \ |
|
"No outer window available!"); \ |
|
return err_rval; \ |
|
} \ |
|
return outer->method args; \ |
|
} \ |
|
PR_END_MACRO |
|
|
|
#define FORWARD_TO_OUTER_OR_THROW(method, args, errorresult, err_rval) \ |
|
PR_BEGIN_MACRO \ |
|
MOZ_RELEASE_ASSERT(IsInnerWindow()); \ |
|
nsGlobalWindow *outer = GetOuterWindowInternal(); \ |
|
if (MOZ_LIKELY(HasActiveDocument())) { \ |
|
return outer->method args; \ |
|
} \ |
|
if (!outer) { \ |
|
NS_WARNING("No outer window available!"); \ |
|
errorresult.Throw(NS_ERROR_NOT_INITIALIZED); \ |
|
} else { \ |
|
errorresult.Throw(NS_ERROR_XPC_SECURITY_MANAGER_VETO); \ |
|
} \ |
|
return err_rval; \ |
|
PR_END_MACRO |
|
|
|
#define FORWARD_TO_OUTER_VOID(method, args) \ |
|
PR_BEGIN_MACRO \ |
|
if (IsInnerWindow()) { \ |
|
nsGlobalWindow *outer = GetOuterWindowInternal(); \ |
|
if (!HasActiveDocument()) { \ |
|
NS_WARNING(outer ? \ |
|
"Inner window does not have active document." : \ |
|
"No outer window available!"); \ |
|
return; \ |
|
} \ |
|
outer->method args; \ |
|
return; \ |
|
} \ |
|
PR_END_MACRO |
|
|
|
#define FORWARD_TO_OUTER_CHROME(method, args, err_rval) \ |
|
PR_BEGIN_MACRO \ |
|
if (IsInnerWindow()) { \ |
|
nsGlobalWindow *outer = GetOuterWindowInternal(); \ |
|
if (!HasActiveDocument()) { \ |
|
NS_WARNING(outer ? \ |
|
"Inner window does not have active document." : \ |
|
"No outer window available!"); \ |
|
return err_rval; \ |
|
} \ |
|
return ((nsGlobalChromeWindow *)outer)->method args; \ |
|
} \ |
|
PR_END_MACRO |
|
|
|
#define FORWARD_TO_INNER_CHROME(method, args, err_rval) \ |
|
PR_BEGIN_MACRO \ |
|
if (IsOuterWindow()) { \ |
|
if (!mInnerWindow) { \ |
|
NS_WARNING("No inner window available!"); \ |
|
return err_rval; \ |
|
} \ |
|
return ((nsGlobalChromeWindow *)mInnerWindow)->method args; \ |
|
} \ |
|
PR_END_MACRO |
|
|
|
#define FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ |
|
PR_BEGIN_MACRO \ |
|
if (IsInnerWindow()) { \ |
|
nsGlobalWindow *outer = GetOuterWindowInternal(); \ |
|
if (!HasActiveDocument()) { \ |
|
NS_WARNING(outer ? \ |
|
"Inner window does not have active document." : \ |
|
"No outer window available!"); \ |
|
return err_rval; \ |
|
} \ |
|
return ((nsGlobalModalWindow *)outer)->method args; \ |
|
} \ |
|
PR_END_MACRO |
|
|
|
#define FORWARD_TO_INNER(method, args, err_rval) \ |
|
PR_BEGIN_MACRO \ |
|
if (IsOuterWindow()) { \ |
|
if (!mInnerWindow) { \ |
|
NS_WARNING("No inner window available!"); \ |
|
return err_rval; \ |
|
} \ |
|
return GetCurrentInnerWindowInternal()->method args; \ |
|
} \ |
|
PR_END_MACRO |
|
|
|
#define FORWARD_TO_INNER_MODAL_CONTENT_WINDOW(method, args, err_rval) \ |
|
PR_BEGIN_MACRO \ |
|
if (IsOuterWindow()) { \ |
|
if (!mInnerWindow) { \ |
|
NS_WARNING("No inner window available!"); \ |
|
return err_rval; \ |
|
} \ |
|
return ((nsGlobalModalWindow*)GetCurrentInnerWindowInternal())->method args; \ |
|
} \ |
|
PR_END_MACRO |
|
|
|
#define FORWARD_TO_INNER_VOID(method, args) \ |
|
PR_BEGIN_MACRO \ |
|
if (IsOuterWindow()) { \ |
|
if (!mInnerWindow) { \ |
|
NS_WARNING("No inner window available!"); \ |
|
return; \ |
|
} \ |
|
GetCurrentInnerWindowInternal()->method args; \ |
|
return; \ |
|
} \ |
|
PR_END_MACRO |
|
|
|
// Same as FORWARD_TO_INNER, but this will create a fresh inner if an |
|
// inner doesn't already exists. |
|
#define FORWARD_TO_INNER_CREATE(method, args, err_rval) \ |
|
PR_BEGIN_MACRO \ |
|
if (IsOuterWindow()) { \ |
|
if (!mInnerWindow) { \ |
|
if (mIsClosed) { \ |
|
return err_rval; \ |
|
} \ |
|
nsCOMPtr<nsIDOMDocument> doc; \ |
|
nsresult fwdic_nr = GetDocument(getter_AddRefs(doc)); \ |
|
NS_ENSURE_SUCCESS(fwdic_nr, err_rval); \ |
|
if (!mInnerWindow) { \ |
|
return err_rval; \ |
|
} \ |
|
} \ |
|
return GetCurrentInnerWindowInternal()->method args; \ |
|
} \ |
|
PR_END_MACRO |
|
|
|
// CIDs |
|
static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID); |
|
|
|
static const char sPopStatePrefStr[] = "browser.history.allowPopState"; |
|
|
|
#define NETWORK_UPLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkupload") |
|
#define NETWORK_DOWNLOAD_EVENT_NAME NS_LITERAL_STRING("moznetworkdownload") |
|
|
|
/** |
|
* An indirect observer object that means we don't have to implement nsIObserver |
|
* on nsGlobalWindow, where any script could see it. |
|
*/ |
|
class nsGlobalWindowObserver final : public nsIObserver, |
|
public nsIInterfaceRequestor |
|
{ |
|
public: |
|
explicit nsGlobalWindowObserver(nsGlobalWindow* aWindow) : mWindow(aWindow) {} |
|
NS_DECL_ISUPPORTS |
|
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override |
|
{ |
|
if (!mWindow) |
|
return NS_OK; |
|
return mWindow->Observe(aSubject, aTopic, aData); |
|
} |
|
void Forget() { mWindow = nullptr; } |
|
NS_IMETHODIMP GetInterface(const nsIID& aIID, void** aResult) override |
|
{ |
|
if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) { |
|
return mWindow->QueryInterface(aIID, aResult); |
|
} |
|
return NS_NOINTERFACE; |
|
} |
|
|
|
private: |
|
~nsGlobalWindowObserver() {} |
|
|
|
// This reference is non-owning and safe because it's cleared by |
|
// nsGlobalWindow::CleanUp(). |
|
nsGlobalWindow* MOZ_NON_OWNING_REF mWindow; |
|
}; |
|
|
|
NS_IMPL_ISUPPORTS(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor) |
|
|
|
nsTimeout::nsTimeout() |
|
: mCleared(false), |
|
mRunning(false), |
|
mIsInterval(false), |
|
mPublicId(0), |
|
mInterval(0), |
|
mFiringDepth(0), |
|
mNestingLevel(0), |
|
mPopupState(openAllowed) |
|
{ |
|
#ifdef DEBUG_jst |
|
{ |
|
extern int gTimeoutCnt; |
|
|
|
++gTimeoutCnt; |
|
} |
|
#endif |
|
|
|
MOZ_COUNT_CTOR(nsTimeout); |
|
} |
|
|
|
nsTimeout::~nsTimeout() |
|
{ |
|
#ifdef DEBUG_jst |
|
{ |
|
extern int gTimeoutCnt; |
|
|
|
--gTimeoutCnt; |
|
} |
|
#endif |
|
|
|
if (mTimer) { |
|
mTimer->Cancel(); |
|
mTimer = nullptr; |
|
} |
|
|
|
MOZ_COUNT_DTOR(nsTimeout); |
|
} |
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsTimeout) |
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsTimeout) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTimeout) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptHandler) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTimeout, AddRef) |
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTimeout, Release) |
|
|
|
// Return true if this timeout has a refcount of 1. This is used to check |
|
// that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout. |
|
bool |
|
nsTimeout::HasRefCntOne() |
|
{ |
|
return mRefCnt.get() == 1; |
|
} |
|
|
|
namespace mozilla { |
|
namespace dom { |
|
extern uint64_t |
|
NextWindowID(); |
|
} // namespace dom |
|
} // namespace mozilla |
|
|
|
nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWindow *aOuterWindow) |
|
: mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0), |
|
mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false), |
|
mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr), |
|
mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false), |
|
mMayHaveMouseEnterLeaveEventListener(false), |
|
mMayHavePointerEnterLeaveEventListener(false), |
|
mIsModalContentWindow(false), |
|
mIsActive(false), mIsBackground(false), |
|
mAudioMuted(false), mAudioVolume(1.0), |
|
mDesktopModeViewport(false), mInnerWindow(nullptr), |
|
mOuterWindow(aOuterWindow), |
|
// Make sure no actual window ends up with mWindowID == 0 |
|
mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false), |
|
mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false) |
|
{} |
|
|
|
nsPIDOMWindow::~nsPIDOMWindow() {} |
|
|
|
// DialogValueHolder CC goop. |
|
NS_IMPL_CYCLE_COLLECTION(DialogValueHolder, mValue) |
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DialogValueHolder) |
|
NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
NS_INTERFACE_MAP_END |
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(DialogValueHolder) |
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(DialogValueHolder) |
|
|
|
//***************************************************************************** |
|
// nsOuterWindowProxy: Outer Window Proxy |
|
//***************************************************************************** |
|
|
|
class nsOuterWindowProxy : public js::Wrapper |
|
{ |
|
public: |
|
MOZ_CONSTEXPR nsOuterWindowProxy() : js::Wrapper(0) { } |
|
|
|
virtual bool finalizeInBackground(JS::Value priv) const override { |
|
return false; |
|
} |
|
|
|
// Standard internal methods |
|
virtual bool getOwnPropertyDescriptor(JSContext* cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, |
|
JS::MutableHandle<JSPropertyDescriptor> desc) |
|
const override; |
|
virtual bool defineProperty(JSContext* cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, |
|
JS::Handle<JSPropertyDescriptor> desc, |
|
JS::ObjectOpResult &result) const override; |
|
virtual bool ownPropertyKeys(JSContext *cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::AutoIdVector &props) const override; |
|
virtual bool delete_(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, |
|
JS::ObjectOpResult &aResult) const override; |
|
virtual bool enumerate(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::MutableHandle<JSObject*> vp) const override; |
|
virtual bool preventExtensions(JSContext* cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::ObjectOpResult& result) const override; |
|
virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) |
|
const override; |
|
virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, bool *bp) const override; |
|
virtual bool get(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<JS::Value> receiver, |
|
JS::Handle<jsid> id, |
|
JS::MutableHandle<JS::Value> vp) const override; |
|
virtual bool set(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, JS::Handle<JS::Value> v, |
|
JS::Handle<JS::Value> receiver, |
|
JS::ObjectOpResult &result) const override; |
|
|
|
// SpiderMonkey extensions |
|
virtual bool getPropertyDescriptor(JSContext* cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, |
|
JS::MutableHandle<JSPropertyDescriptor> desc) |
|
const override; |
|
virtual bool hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, bool *bp) const override; |
|
virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::AutoIdVector &props) const override; |
|
virtual const char *className(JSContext *cx, |
|
JS::Handle<JSObject*> wrapper) const override; |
|
|
|
virtual void finalize(JSFreeOp *fop, JSObject *proxy) const override; |
|
|
|
virtual bool isCallable(JSObject *obj) const override { |
|
return false; |
|
} |
|
virtual bool isConstructor(JSObject *obj) const override { |
|
return false; |
|
} |
|
|
|
virtual bool watch(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const override; |
|
virtual bool unwatch(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id) const override; |
|
|
|
static void ObjectMoved(JSObject *obj, const JSObject *old); |
|
|
|
static const nsOuterWindowProxy singleton; |
|
|
|
protected: |
|
static nsGlobalWindow* GetWindow(JSObject *proxy) |
|
{ |
|
return nsGlobalWindow::FromSupports( |
|
static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate())); |
|
} |
|
|
|
// False return value means we threw an exception. True return value |
|
// but false "found" means we didn't have a subframe at that index. |
|
bool GetSubframeWindow(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, |
|
JS::MutableHandle<JS::Value> vp, |
|
bool &found) const; |
|
|
|
// Returns a non-null window only if id is an index and we have a |
|
// window at that index. |
|
already_AddRefed<nsIDOMWindow> GetSubframeWindow(JSContext *cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id) const; |
|
|
|
bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy, |
|
JS::AutoIdVector &props) const; |
|
}; |
|
|
|
const js::Class OuterWindowProxyClass = |
|
PROXY_CLASS_WITH_EXT( |
|
"Proxy", |
|
0, /* additional class flags */ |
|
PROXY_MAKE_EXT( |
|
nullptr, /* outerObject */ |
|
js::proxy_innerObject, |
|
false, /* isWrappedNative */ |
|
nsOuterWindowProxy::ObjectMoved |
|
)); |
|
|
|
const char * |
|
nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy) const |
|
{ |
|
MOZ_ASSERT(js::IsProxy(proxy)); |
|
|
|
return "Window"; |
|
} |
|
|
|
void |
|
nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy) const |
|
{ |
|
nsGlobalWindow* global = GetWindow(proxy); |
|
if (global) { |
|
global->ClearWrapper(); |
|
|
|
// Ideally we would use OnFinalize here, but it's possible that |
|
// EnsureScriptEnvironment will later be called on the window, and we don't |
|
// want to create a new script object in that case. Therefore, we need to |
|
// write a non-null value that will reliably crash when dereferenced. |
|
global->PoisonOuterWindowProxy(proxy); |
|
} |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::getPropertyDescriptor(JSContext* cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, |
|
JS::MutableHandle<JSPropertyDescriptor> desc) const |
|
{ |
|
// The only thing we can do differently from js::Wrapper is shadow stuff with |
|
// our indexed properties, so we can just try getOwnPropertyDescriptor and if |
|
// that gives us nothing call on through to js::Wrapper. |
|
desc.object().set(nullptr); |
|
if (!getOwnPropertyDescriptor(cx, proxy, id, desc)) { |
|
return false; |
|
} |
|
|
|
if (desc.object()) { |
|
return true; |
|
} |
|
|
|
return js::Wrapper::getPropertyDescriptor(cx, proxy, id, desc); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::getOwnPropertyDescriptor(JSContext* cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, |
|
JS::MutableHandle<JSPropertyDescriptor> desc) |
|
const |
|
{ |
|
bool found; |
|
if (!GetSubframeWindow(cx, proxy, id, desc.value(), found)) { |
|
return false; |
|
} |
|
if (found) { |
|
FillPropertyDescriptor(desc, proxy, true); |
|
return true; |
|
} |
|
// else fall through to js::Wrapper |
|
|
|
return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::defineProperty(JSContext* cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, |
|
JS::Handle<JSPropertyDescriptor> desc, |
|
JS::ObjectOpResult &result) const |
|
{ |
|
int32_t index = GetArrayIndexFromId(cx, id); |
|
if (IsArrayIndex(index)) { |
|
// Spec says to Reject whether this is a supported index or not, |
|
// since we have no indexed setter or indexed creator. It is up |
|
// to the caller to decide whether to throw a TypeError. |
|
return result.failCantDefineWindowElement(); |
|
} |
|
|
|
// For now, allow chrome code to define non-configurable properties |
|
// on windows, until we sort out what exactly the addon SDK is |
|
// doing. In the meantime, this still allows us to test web compat |
|
// behavior. |
|
if (false && !desc.configurable() && !nsContentUtils::IsCallerChrome()) { |
|
return ThrowErrorMessage(cx, MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW); |
|
} |
|
|
|
return js::Wrapper::defineProperty(cx, proxy, id, desc, result); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::ownPropertyKeys(JSContext *cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::AutoIdVector &props) const |
|
{ |
|
// Just our indexed stuff followed by our "normal" own property names. |
|
if (!AppendIndexedPropertyNames(cx, proxy, props)) { |
|
return false; |
|
} |
|
|
|
JS::AutoIdVector innerProps(cx); |
|
if (!js::Wrapper::ownPropertyKeys(cx, proxy, innerProps)) { |
|
return false; |
|
} |
|
return js::AppendUnique(cx, props, innerProps); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::delete_(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, JS::ObjectOpResult &result) const |
|
{ |
|
if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) { |
|
// Fail (which means throw if strict, else return false). |
|
return result.failCantDeleteWindowElement(); |
|
} |
|
|
|
int32_t index = GetArrayIndexFromId(cx, id); |
|
if (IsArrayIndex(index)) { |
|
// Indexed, but not supported. Spec says return true. |
|
return result.succeed(); |
|
} |
|
|
|
return js::Wrapper::delete_(cx, proxy, id, result); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::preventExtensions(JSContext* cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::ObjectOpResult& result) const |
|
{ |
|
// If [[Extensible]] could be false, then navigating a window could navigate |
|
// to a window that's [[Extensible]] after being at one that wasn't: an |
|
// invariant violation. So never change a window's extensibility. |
|
return result.failCantPreventExtensions(); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
bool *extensible) const |
|
{ |
|
// See above. |
|
*extensible = true; |
|
return true; |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::has(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, bool *bp) const |
|
{ |
|
if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) { |
|
*bp = true; |
|
return true; |
|
} |
|
|
|
return js::Wrapper::has(cx, proxy, id, bp); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, bool *bp) const |
|
{ |
|
if (nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id)) { |
|
*bp = true; |
|
return true; |
|
} |
|
|
|
return js::Wrapper::hasOwn(cx, proxy, id, bp); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::get(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<JS::Value> receiver, |
|
JS::Handle<jsid> id, |
|
JS::MutableHandle<JS::Value> vp) const |
|
{ |
|
if (id == nsDOMClassInfo::sWrappedJSObject_id && |
|
xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) { |
|
vp.set(JS::ObjectValue(*proxy)); |
|
return true; |
|
} |
|
|
|
bool found; |
|
if (!GetSubframeWindow(cx, proxy, id, vp, found)) { |
|
return false; |
|
} |
|
if (found) { |
|
return true; |
|
} |
|
// Else fall through to js::Wrapper |
|
|
|
return js::Wrapper::get(cx, proxy, receiver, id, vp); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::set(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, |
|
JS::Handle<JS::Value> v, |
|
JS::Handle<JS::Value> receiver, |
|
JS::ObjectOpResult &result) const |
|
{ |
|
int32_t index = GetArrayIndexFromId(cx, id); |
|
if (IsArrayIndex(index)) { |
|
// Reject the set. It's up to the caller to decide whether to throw a |
|
// TypeError. If the caller is strict mode JS code, it'll throw. |
|
return result.failReadOnly(); |
|
} |
|
|
|
return js::Wrapper::set(cx, proxy, id, v, receiver, result); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::AutoIdVector &props) const |
|
{ |
|
// BaseProxyHandler::keys seems to do what we want here: call |
|
// ownPropertyKeys and then filter out the non-enumerable properties. |
|
return js::BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::enumerate(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::MutableHandle<JSObject*> objp) const |
|
{ |
|
// BaseProxyHandler::enumerate seems to do what we want here: fall |
|
// back on the property names returned from js::GetPropertyKeys() |
|
return js::BaseProxyHandler::enumerate(cx, proxy, objp); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::GetSubframeWindow(JSContext *cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, |
|
JS::MutableHandle<JS::Value> vp, |
|
bool& found) const |
|
{ |
|
nsCOMPtr<nsIDOMWindow> frame = GetSubframeWindow(cx, proxy, id); |
|
if (!frame) { |
|
found = false; |
|
return true; |
|
} |
|
|
|
found = true; |
|
// Just return the window's global |
|
nsGlobalWindow* global = static_cast<nsGlobalWindow*>(frame.get()); |
|
global->EnsureInnerWindow(); |
|
JSObject* obj = global->FastGetGlobalJSObject(); |
|
// This null check fixes a hard-to-reproduce crash that occurs when we |
|
// get here when we're mid-call to nsDocShell::Destroy. See bug 640904 |
|
// comment 105. |
|
if (MOZ_UNLIKELY(!obj)) { |
|
return xpc::Throw(cx, NS_ERROR_FAILURE); |
|
} |
|
|
|
vp.setObject(*obj); |
|
return JS_WrapValue(cx, vp); |
|
} |
|
|
|
already_AddRefed<nsIDOMWindow> |
|
nsOuterWindowProxy::GetSubframeWindow(JSContext *cx, |
|
JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id) const |
|
{ |
|
int32_t index = GetArrayIndexFromId(cx, id); |
|
if (!IsArrayIndex(index)) { |
|
return nullptr; |
|
} |
|
|
|
nsGlobalWindow* win = GetWindow(proxy); |
|
MOZ_ASSERT(win->IsOuterWindow()); |
|
return win->IndexedGetterOuter(index); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy, |
|
JS::AutoIdVector &props) const |
|
{ |
|
uint32_t length = GetWindow(proxy)->Length(); |
|
MOZ_ASSERT(int32_t(length) >= 0); |
|
if (!props.reserve(props.length() + length)) { |
|
return false; |
|
} |
|
for (int32_t i = 0; i < int32_t(length); ++i) { |
|
props.append(INT_TO_JSID(i)); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::watch(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id, JS::Handle<JSObject*> callable) const |
|
{ |
|
return js::WatchGuts(cx, proxy, id, callable); |
|
} |
|
|
|
bool |
|
nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy, |
|
JS::Handle<jsid> id) const |
|
{ |
|
return js::UnwatchGuts(cx, proxy, id); |
|
} |
|
|
|
void |
|
nsOuterWindowProxy::ObjectMoved(JSObject *obj, const JSObject *old) |
|
{ |
|
nsGlobalWindow* global = GetWindow(obj); |
|
if (global) { |
|
global->UpdateWrapper(obj, old); |
|
} |
|
} |
|
|
|
const nsOuterWindowProxy |
|
nsOuterWindowProxy::singleton; |
|
|
|
class nsChromeOuterWindowProxy : public nsOuterWindowProxy |
|
{ |
|
public: |
|
MOZ_CONSTEXPR nsChromeOuterWindowProxy() : nsOuterWindowProxy() { } |
|
|
|
virtual const char *className(JSContext *cx, JS::Handle<JSObject*> wrapper) const override; |
|
|
|
static const nsChromeOuterWindowProxy singleton; |
|
}; |
|
|
|
const char * |
|
nsChromeOuterWindowProxy::className(JSContext *cx, |
|
JS::Handle<JSObject*> proxy) const |
|
{ |
|
MOZ_ASSERT(js::IsProxy(proxy)); |
|
|
|
return "ChromeWindow"; |
|
} |
|
|
|
const nsChromeOuterWindowProxy |
|
nsChromeOuterWindowProxy::singleton; |
|
|
|
static JSObject* |
|
NewOuterWindowProxy(JSContext *cx, JS::Handle<JSObject*> global, bool isChrome) |
|
{ |
|
JSAutoCompartment ac(cx, global); |
|
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(global) == global); |
|
|
|
js::WrapperOptions options; |
|
options.setClass(&OuterWindowProxyClass); |
|
options.setSingleton(true); |
|
JSObject *obj = js::Wrapper::New(cx, global, |
|
isChrome ? &nsChromeOuterWindowProxy::singleton |
|
: &nsOuterWindowProxy::singleton, |
|
options); |
|
|
|
NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class"); |
|
return obj; |
|
} |
|
|
|
//***************************************************************************** |
|
//*** nsGlobalWindow: Object Management |
|
//***************************************************************************** |
|
|
|
nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) |
|
: nsPIDOMWindow(aOuterWindow), |
|
mIdleFuzzFactor(0), |
|
mIdleCallbackIndex(-1), |
|
mCurrentlyIdle(false), |
|
mAddActiveEventFuzzTime(true), |
|
mIsFrozen(false), |
|
mFullScreen(false), |
|
mFullscreenMode(false), |
|
mIsClosed(false), |
|
mInClose(false), |
|
mHavePendingClose(false), |
|
mHadOriginalOpener(false), |
|
mIsPopupSpam(false), |
|
mBlockScriptedClosingFlag(false), |
|
mWasOffline(false), |
|
mNotifyIdleObserversIdleOnThaw(false), |
|
mNotifyIdleObserversActiveOnThaw(false), |
|
mCreatingInnerWindow(false), |
|
mIsChrome(false), |
|
mCleanMessageManager(false), |
|
mNeedsFocus(true), |
|
mHasFocus(false), |
|
#if defined(XP_MACOSX) |
|
mShowAccelerators(false), |
|
mShowFocusRings(false), |
|
#else |
|
mShowAccelerators(true), |
|
mShowFocusRings(true), |
|
#endif |
|
mShowFocusRingForContent(false), |
|
mFocusByKeyOccurred(false), |
|
mInnerObjectsFreed(false), |
|
mHasGamepad(false), |
|
#ifdef MOZ_GAMEPAD |
|
mHasSeenGamepadInput(false), |
|
#endif |
|
mNotifiedIDDestroyed(false), |
|
mAllowScriptsToClose(false), |
|
mTimeoutInsertionPoint(nullptr), |
|
mTimeoutPublicIdCounter(1), |
|
mTimeoutFiringDepth(0), |
|
mTimeoutsSuspendDepth(0), |
|
mFocusMethod(0), |
|
mSerial(0), |
|
#ifdef DEBUG |
|
mSetOpenerWindowCalled(false), |
|
#endif |
|
#ifdef MOZ_B2G |
|
mNetworkUploadObserverEnabled(false), |
|
mNetworkDownloadObserverEnabled(false), |
|
#endif |
|
mCleanedUp(false), |
|
mDialogAbuseCount(0), |
|
mAreDialogsEnabled(true), |
|
mCanSkipCCGeneration(0), |
|
mVRDevicesInitialized(false) |
|
{ |
|
AssertIsOnMainThread(); |
|
|
|
nsLayoutStatics::AddRef(); |
|
|
|
// Initialize the PRCList (this). |
|
PR_INIT_CLIST(this); |
|
|
|
if (aOuterWindow) { |
|
// |this| is an inner window, add this inner window to the outer |
|
// window list of inners. |
|
PR_INSERT_AFTER(this, aOuterWindow); |
|
|
|
mObserver = new nsGlobalWindowObserver(this); |
|
if (mObserver) { |
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
if (os) { |
|
// Watch for online/offline status changes so we can fire events. Use |
|
// a strong reference. |
|
os->AddObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, |
|
false); |
|
|
|
// Watch for dom-storage2-changed so we can fire storage |
|
// events. Use a strong reference. |
|
os->AddObserver(mObserver, "dom-storage2-changed", false); |
|
} |
|
|
|
Preferences::AddStrongObserver(mObserver, "intl.accept_languages"); |
|
} |
|
} else { |
|
// |this| is an outer window. Outer windows start out frozen and |
|
// remain frozen until they get an inner window, so freeze this |
|
// outer window here. |
|
Freeze(); |
|
} |
|
|
|
// We could have failed the first time through trying |
|
// to create the entropy collector, so we should |
|
// try to get one until we succeed. |
|
|
|
gRefCnt++; |
|
|
|
if (gRefCnt == 1) { |
|
Preferences::AddIntVarCache(&gMinTimeoutValue, |
|
"dom.min_timeout_value", |
|
DEFAULT_MIN_TIMEOUT_VALUE); |
|
Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue, |
|
"dom.min_background_timeout_value", |
|
DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE); |
|
Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled, |
|
"dom.idle-observers-api.fuzz_time.disabled", |
|
false); |
|
} |
|
|
|
if (gDumpFile == nullptr) { |
|
const nsAdoptingCString& fname = |
|
Preferences::GetCString("browser.dom.window.dump.file"); |
|
if (!fname.IsEmpty()) { |
|
// if this fails to open, Dump() knows to just go to stdout |
|
// on null. |
|
gDumpFile = fopen(fname, "wb+"); |
|
} else { |
|
gDumpFile = stdout; |
|
} |
|
} |
|
|
|
mSerial = ++gSerialCounter; |
|
|
|
#ifdef DEBUG |
|
if (!PR_GetEnv("MOZ_QUIET")) { |
|
printf_stderr("++DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p]\n", |
|
gRefCnt, |
|
static_cast<void*>(ToCanonicalSupports(this)), |
|
getpid(), |
|
gSerialCounter, |
|
static_cast<void*>(ToCanonicalSupports(aOuterWindow))); |
|
} |
|
#endif |
|
|
|
if (gDOMLeakPRLog) |
|
MOZ_LOG(gDOMLeakPRLog, LogLevel::Debug, |
|
("DOMWINDOW %p created outer=%p", this, aOuterWindow)); |
|
|
|
NS_ASSERTION(sWindowsById, "Windows hash table must be created!"); |
|
NS_ASSERTION(!sWindowsById->Get(mWindowID), |
|
"This window shouldn't be in the hash table yet!"); |
|
// We seem to see crashes in release builds because of null |sWindowsById|. |
|
if (sWindowsById) { |
|
sWindowsById->Put(mWindowID, this); |
|
} |
|
} |
|
|
|
#ifdef DEBUG |
|
|
|
/* static */ |
|
void |
|
nsGlobalWindow::AssertIsOnMainThread() |
|
{ |
|
MOZ_ASSERT(NS_IsMainThread()); |
|
} |
|
|
|
#endif // DEBUG |
|
|
|
/* static */ |
|
void |
|
nsGlobalWindow::Init() |
|
{ |
|
AssertIsOnMainThread(); |
|
|
|
CallGetService(NS_ENTROPYCOLLECTOR_CONTRACTID, &gEntropyCollector); |
|
NS_ASSERTION(gEntropyCollector, |
|
"gEntropyCollector should have been initialized!"); |
|
|
|
gDOMLeakPRLog = PR_NewLogModule("DOMLeak"); |
|
NS_ASSERTION(gDOMLeakPRLog, "gDOMLeakPRLog should have been initialized!"); |
|
|
|
sWindowsById = new WindowByIdTable(); |
|
} |
|
|
|
nsGlobalWindow::~nsGlobalWindow() |
|
{ |
|
AssertIsOnMainThread(); |
|
|
|
DisconnectEventTargetObjects(); |
|
|
|
// We have to check if sWindowsById isn't null because ::Shutdown might have |
|
// been called. |
|
if (sWindowsById) { |
|
NS_ASSERTION(sWindowsById->Get(mWindowID), |
|
"This window should be in the hash table"); |
|
sWindowsById->Remove(mWindowID); |
|
} |
|
|
|
--gRefCnt; |
|
|
|
#ifdef DEBUG |
|
if (!PR_GetEnv("MOZ_QUIET")) { |
|
nsAutoCString url; |
|
if (mLastOpenedURI) { |
|
mLastOpenedURI->GetSpec(url); |
|
|
|
// Data URLs can be very long, so truncate to avoid flooding the log. |
|
const uint32_t maxURLLength = 1000; |
|
if (url.Length() > maxURLLength) { |
|
url.Truncate(maxURLLength); |
|
} |
|
} |
|
|
|
nsGlobalWindow* outer = static_cast<nsGlobalWindow*>(mOuterWindow.get()); |
|
printf_stderr("--DOMWINDOW == %d (%p) [pid = %d] [serial = %d] [outer = %p] [url = %s]\n", |
|
gRefCnt, |
|
static_cast<void*>(ToCanonicalSupports(this)), |
|
getpid(), |
|
mSerial, |
|
static_cast<void*>(ToCanonicalSupports(outer)), |
|
url.get()); |
|
} |
|
#endif |
|
|
|
if (gDOMLeakPRLog) |
|
MOZ_LOG(gDOMLeakPRLog, LogLevel::Debug, |
|
("DOMWINDOW %p destroyed", this)); |
|
|
|
if (IsOuterWindow()) { |
|
JSObject *proxy = GetWrapperPreserveColor(); |
|
if (proxy) { |
|
js::SetProxyExtra(proxy, 0, js::PrivateValue(nullptr)); |
|
} |
|
|
|
// An outer window is destroyed with inner windows still possibly |
|
// alive, iterate through the inner windows and null out their |
|
// back pointer to this outer, and pull them out of the list of |
|
// inner windows. |
|
|
|
nsGlobalWindow *w; |
|
while ((w = (nsGlobalWindow *)PR_LIST_HEAD(this)) != this) { |
|
PR_REMOVE_AND_INIT_LINK(w); |
|
} |
|
|
|
DropOuterWindowDocs(); |
|
} else { |
|
Telemetry::Accumulate(Telemetry::INNERWINDOWS_WITH_MUTATION_LISTENERS, |
|
mMutationBits ? 1 : 0); |
|
|
|
if (mListenerManager) { |
|
mListenerManager->Disconnect(); |
|
mListenerManager = nullptr; |
|
} |
|
|
|
// An inner window is destroyed, pull it out of the outer window's |
|
// list if inner windows. |
|
|
|
PR_REMOVE_LINK(this); |
|
|
|
// If our outer window's inner window is this window, null out the |
|
// outer window's reference to this window that's being deleted. |
|
nsGlobalWindow *outer = GetOuterWindowInternal(); |
|
if (outer) { |
|
outer->MaybeClearInnerWindow(this); |
|
} |
|
} |
|
|
|
// Outer windows are always supposed to call CleanUp before letting themselves |
|
// be destroyed. And while CleanUp generally seems to be intended to clean up |
|
// outers, we've historically called it for both. Changing this would probably |
|
// involve auditing all of the references that inners and outers can have, and |
|
// separating the handling into CleanUp() and FreeInnerObjects. |
|
if (IsInnerWindow()) { |
|
CleanUp(); |
|
} else { |
|
MOZ_ASSERT(mCleanedUp); |
|
} |
|
|
|
nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); |
|
if (ac) |
|
ac->RemoveWindowAsListener(this); |
|
|
|
nsLayoutStatics::Release(); |
|
} |
|
|
|
void |
|
nsGlobalWindow::AddEventTargetObject(DOMEventTargetHelper* aObject) |
|
{ |
|
MOZ_ASSERT(IsInnerWindow()); |
|
mEventTargetObjects.PutEntry(aObject); |
|
} |
|
|
|
void |
|
nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject) |
|
{ |
|
MOZ_ASSERT(IsInnerWindow()); |
|
mEventTargetObjects.RemoveEntry(aObject); |
|
} |
|
|
|
void |
|
nsGlobalWindow::DisconnectEventTargetObjects() |
|
{ |
|
for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done(); |
|
iter.Next()) { |
|
nsRefPtr<DOMEventTargetHelper> target = iter.Get()->GetKey(); |
|
target->DisconnectFromOwner(); |
|
} |
|
mEventTargetObjects.Clear(); |
|
} |
|
|
|
// static |
|
void |
|
nsGlobalWindow::ShutDown() |
|
{ |
|
AssertIsOnMainThread(); |
|
|
|
if (gDumpFile && gDumpFile != stdout) { |
|
fclose(gDumpFile); |
|
} |
|
gDumpFile = nullptr; |
|
|
|
NS_IF_RELEASE(gEntropyCollector); |
|
|
|
delete sWindowsById; |
|
sWindowsById = nullptr; |
|
} |
|
|
|
// static |
|
void |
|
nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow) |
|
{ |
|
if (aWindow->mCachedXBLPrototypeHandlers && |
|
aWindow->mCachedXBLPrototypeHandlers->Count() > 0) { |
|
aWindow->mCachedXBLPrototypeHandlers->Clear(); |
|
} |
|
} |
|
|
|
void |
|
nsGlobalWindow::MaybeForgiveSpamCount() |
|
{ |
|
if (IsOuterWindow() && |
|
IsPopupSpamWindow()) |
|
{ |
|
SetPopupSpamWindow(false); |
|
--gOpenPopupSpamCount; |
|
NS_ASSERTION(gOpenPopupSpamCount >= 0, |
|
"Unbalanced decrement of gOpenPopupSpamCount"); |
|
} |
|
} |
|
|
|
void |
|
nsGlobalWindow::DropOuterWindowDocs() |
|
{ |
|
MOZ_ASSERT(IsOuterWindow()); |
|
MOZ_ASSERT_IF(mDoc, !mDoc->EventHandlingSuppressed()); |
|
mDoc = nullptr; |
|
mSuspendedDoc = nullptr; |
|
} |
|
|
|
void |
|
nsGlobalWindow::CleanUp() |
|
{ |
|
// Guarantee idempotence. |
|
if (mCleanedUp) |
|
return; |
|
mCleanedUp = true; |
|
|
|
StartDying(); |
|
|
|
DisconnectEventTargetObjects(); |
|
|
|
if (mObserver) { |
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
if (os) { |
|
os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC); |
|
os->RemoveObserver(mObserver, "dom-storage2-changed"); |
|
} |
|
|
|
#ifdef MOZ_B2G |
|
DisableNetworkEvent(NS_NETWORK_UPLOAD_EVENT); |
|
DisableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT); |
|
#endif // MOZ_B2G |
|
|
|
if (mIdleService) { |
|
mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S); |
|
} |
|
|
|
Preferences::RemoveObserver(mObserver, "intl.accept_languages"); |
|
|
|
// Drop its reference to this dying window, in case for some bogus reason |
|
// the object stays around. |
|
mObserver->Forget(); |
|
} |
|
|
|
if (mNavigator) { |
|
mNavigator->Invalidate(); |
|
mNavigator = nullptr; |
|
} |
|
|
|
mScreen = nullptr; |
|
mMenubar = nullptr; |
|
mToolbar = nullptr; |
|
mLocationbar = nullptr; |
|
mPersonalbar = nullptr; |
|
mStatusbar = nullptr; |
|
mScrollbars = nullptr; |
|
mLocation = nullptr; |
|
mHistory = nullptr; |
|
mFrames = nullptr; |
|
mWindowUtils = nullptr; |
|
mApplicationCache = nullptr; |
|
mIndexedDB = nullptr; |
|
|
|
mConsole = nullptr; |
|
|
|
mExternal = nullptr; |
|
|
|
mMozSelfSupport = nullptr; |
|
|
|
mPerformance = nullptr; |
|
|
|
#ifdef MOZ_WEBSPEECH |
|
mSpeechSynthesis = nullptr; |
|
#endif |
|
|
|
ClearControllers(); |
|
|
|
mOpener = nullptr; // Forces Release |
|
if (mContext) { |
|
mContext = nullptr; // Forces Release |
|
} |
|
mChromeEventHandler = nullptr; // Forces Release |
|
mParentTarget = nullptr; |
|
|
|
if (IsOuterWindow()) { |
|
nsGlobalWindow* inner = GetCurrentInnerWindowInternal(); |
|
if (inner) { |
|
inner->CleanUp(); |
|
} |
|
} |
|
|
|
if (IsInnerWindow()) { |
|
DisableGamepadUpdates(); |
|
mHasGamepad = false; |
|
} else { |
|
MOZ_ASSERT(!mHasGamepad); |
|
} |
|
|
|
if (mCleanMessageManager) { |
|
MOZ_ASSERT(mIsChrome, "only chrome should have msg manager cleaned"); |
|
nsGlobalChromeWindow *asChrome = static_cast<nsGlobalChromeWindow*>(this); |
|
if (asChrome->mMessageManager) { |
|
static_cast<nsFrameMessageManager*>( |
|
asChrome->mMessageManager.get())->Disconnect(); |
|
} |
|
} |
|
|
|
mArguments = nullptr; |
|
mDialogArguments = nullptr; |
|
|
|
CleanupCachedXBLHandlers(this); |
|
|
|
for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { |
|
mAudioContexts[i]->Shutdown(); |
|
} |
|
mAudioContexts.Clear(); |
|
|
|
if (mIdleTimer) { |
|
mIdleTimer->Cancel(); |
|
mIdleTimer = nullptr; |
|
} |
|
|
|
DisableTimeChangeNotifications(); |
|
} |
|
|
|
void |
|
nsGlobalWindow::ClearControllers() |
|
{ |
|
if (mControllers) { |
|
uint32_t count; |
|
mControllers->GetControllerCount(&count); |
|
|
|
while (count--) { |
|
nsCOMPtr<nsIController> controller; |
|
mControllers->GetControllerAt(count, getter_AddRefs(controller)); |
|
|
|
nsCOMPtr<nsIControllerContext> context = do_QueryInterface(controller); |
|
if (context) |
|
context->SetCommandContext(nullptr); |
|
} |
|
|
|
mControllers = nullptr; |
|
} |
|
} |
|
|
|
void |
|
nsGlobalWindow::FreeInnerObjects(bool aForDocumentOpen) |
|
{ |
|
NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window"); |
|
|
|
// Make sure that this is called before we null out the document and |
|
// other members that the window destroyed observers could |
|
// re-create. |
|
NotifyDOMWindowDestroyed(this); |
|
|
|
mInnerObjectsFreed = true; |
|
|
|
// Kill all of the workers for this window. |
|
mozilla::dom::workers::CancelWorkersForWindow(this); |
|
|
|
ClearAllTimeouts(); |
|
|
|
if (mIdleTimer) { |
|
mIdleTimer->Cancel(); |
|
mIdleTimer = nullptr; |
|
} |
|
|
|
mIdleObservers.Clear(); |
|
|
|
mChromeEventHandler = nullptr; |
|
|
|
if (mListenerManager) { |
|
mListenerManager->Disconnect(); |
|
mListenerManager = nullptr; |
|
} |
|
|
|
mLocation = nullptr; |
|
mHistory = nullptr; |
|
|
|
if (mNavigator) { |
|
mNavigator->OnNavigation(); |
|
mNavigator->Invalidate(); |
|
mNavigator = nullptr; |
|
} |
|
|
|
if (mScreen) { |
|
mScreen = nullptr; |
|
} |
|
|
|
if (mDoc) { |
|
// Remember the document's principal and URI. |
|
mDocumentPrincipal = mDoc->NodePrincipal(); |
|
mDocumentURI = mDoc->GetDocumentURI(); |
|
mDocBaseURI = mDoc->GetDocBaseURI(); |
|
|
|
if (!aForDocumentOpen) { |
|
while (mDoc->EventHandlingSuppressed()) { |
|
mDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, false); |
|
} |
|
} |
|
|
|
// Note: we don't have to worry about eAnimationsOnly suppressions because |
|
// they won't leak. |
|
} |
|
|
|
// Remove our reference to the document and the document principal. |
|
mFocusedNode = nullptr; |
|
|
|
if (mApplicationCache) { |
|
static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->Disconnect(); |
|
mApplicationCache = nullptr; |
|
} |
|
|
|
mIndexedDB = nullptr; |
|
|
|
NotifyWindowIDDestroyed("inner-window-destroyed"); |
|
|
|
CleanupCachedXBLHandlers(this); |
|
|
|
for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) { |
|
mAudioContexts[i]->Shutdown(); |
|
} |
|
mAudioContexts.Clear(); |
|
|
|
#ifdef MOZ_GAMEPAD |
|
DisableGamepadUpdates(); |
|
mHasGamepad = false; |
|
mGamepads.Clear(); |
|
#endif |
|
} |
|
|
|
//***************************************************************************** |
|
// nsGlobalWindow::nsISupports |
|
//***************************************************************************** |
|
|
|
// QueryInterface implementation for nsGlobalWindow |
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow) |
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
// Make sure this matches the cast in nsGlobalWindow::FromWrapper() |
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget) |
|
NS_INTERFACE_MAP_ENTRY(nsIDOMWindow) |
|
if (aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) { |
|
foundInterface = static_cast<nsIDOMWindowInternal*>(this); |
|
if (!sWarnedAboutWindowInternal) { |
|
sWarnedAboutWindowInternal = true; |
|
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
NS_LITERAL_CSTRING("Extensions"), mDoc, |
|
nsContentUtils::eDOM_PROPERTIES, |
|
"nsIDOMWindowInternalWarning"); |
|
} |
|
} else |
|
NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) |
|
NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject) |
|
NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) |
|
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) |
|
NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) |
|
NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow) |
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) |
|
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) |
|
NS_INTERFACE_MAP_END |
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow) |
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow) |
|
|
|
static PLDHashOperator |
|
MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure) |
|
{ |
|
JS::ExposeObjectToActiveJS(aData); |
|
return PL_DHASH_NEXT; |
|
} |
|
|
|
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow) |
|
if (tmp->IsBlackForCC(false)) { |
|
if (nsCCUncollectableMarker::InGeneration(tmp->mCanSkipCCGeneration)) { |
|
return true; |
|
} |
|
tmp->mCanSkipCCGeneration = nsCCUncollectableMarker::sGeneration; |
|
if (tmp->mCachedXBLPrototypeHandlers) { |
|
tmp->mCachedXBLPrototypeHandlers->Enumerate(MarkXBLHandlers, nullptr); |
|
} |
|
if (EventListenerManager* elm = tmp->GetExistingListenerManager()) { |
|
elm->MarkForCC(); |
|
} |
|
tmp->UnmarkGrayTimers(); |
|
return true; |
|
} |
|
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END |
|
|
|
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow) |
|
return tmp->IsBlackForCC(true); |
|
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END |
|
|
|
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGlobalWindow) |
|
return tmp->IsBlackForCC(false); |
|
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END |
|
|
|
inline void |
|
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, |
|
IdleObserverHolder& aField, |
|
const char* aName, |
|
unsigned aFlags) |
|
{ |
|
CycleCollectionNoteChild(aCallback, aField.mIdleObserver.get(), aName, aFlags); |
|
} |
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalWindow) |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow) |
|
if (MOZ_UNLIKELY(cb.WantDebugInfo())) { |
|
char name[512]; |
|
nsAutoCString uri; |
|
if (tmp->mDoc && tmp->mDoc->GetDocumentURI()) { |
|
tmp->mDoc->GetDocumentURI()->GetSpec(uri); |
|
} |
|
PR_snprintf(name, sizeof(name), "nsGlobalWindow #%llu %s %s", |
|
tmp->mWindowID, tmp->IsInnerWindow() ? "inner" : "outer", |
|
uri.get()); |
|
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); |
|
} else { |
|
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get()) |
|
} |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext) |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) |
|
|
|
#ifdef MOZ_WEBSPEECH |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis) |
|
#endif |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow) |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) |
|
|
|
for (nsTimeout* timeout = tmp->mTimeouts.getFirst(); |
|
timeout; |
|
timeout = timeout->getNext()) { |
|
cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout)); |
|
} |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory) |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplicationCache) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers) |
|
|
|
#ifdef MOZ_GAMEPAD |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads) |
|
#endif |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDevices) |
|
|
|
// Traverse stuff from nsPIDOMWindow |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedNode) |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal) |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMozSelfSupport) |
|
|
|
tmp->TraverseHostObjectURIs(cb); |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) |
|
nsGlobalWindow::CleanupCachedXBLHandlers(tmp); |
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext) |
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) |
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) |
|
|
|
#ifdef MOZ_WEBSPEECH |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis) |
|
#endif |
|
|
|
if (tmp->mOuterWindow) { |
|
static_cast<nsGlobalWindow*>(tmp->mOuterWindow.get())->MaybeClearInnerWindow(tmp); |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow) |
|
} |
|
|
|
if (tmp->mListenerManager) { |
|
tmp->mListenerManager->Disconnect(); |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager) |
|
} |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHistory) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStorage) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStorage) |
|
if (tmp->mApplicationCache) { |
|
static_cast<nsDOMOfflineResourceList*>(tmp->mApplicationCache.get())->Disconnect(); |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplicationCache) |
|
} |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedDoc) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentPrincipal) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleService) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWakeLock) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingStorageEvents) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleObservers) |
|
|
|
#ifdef MOZ_GAMEPAD |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads) |
|
#endif |
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDevices) |
|
|
|
// Unlink stuff from nsPIDOMWindow |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedNode) |
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal) |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport) |
|
|
|
tmp->UnlinkHostObjectURIs(); |
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER |
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
|
|
#ifdef DEBUG |
|
void |
|
nsGlobalWindow::RiskyUnlink() |
|
{ |
|
NS_CYCLE_COLLECTION_INNERNAME.Unlink(this); |
|
} |
|
#endif |
|
|
|
struct TraceData |
|
{ |
|
const TraceCallbacks& callbacks; |
|
void* closure; |
|
}; |
|
|
|
static PLDHashOperator |
|
TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure) |
|
{ |
|
TraceData* data = static_cast<TraceData*>(aClosure); |
|
data->callbacks.Trace(&aData, "Cached XBL prototype handler", data->closure); |
|
return PL_DHASH_NEXT; |
|
} |
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow) |
|
if (tmp->mCachedXBLPrototypeHandlers) { |
|
TraceData data = { aCallbacks, aClosure }; |
|
tmp->mCachedXBLPrototypeHandlers->Enumerate(TraceXBLHandlers, &data); |
|
} |
|
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER |
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END |
|
|
|
bool |
|
nsGlobalWindow::IsBlackForCC(bool aTracingNeeded) |
|
{ |
|
if (!nsCCUncollectableMarker::sGeneration) { |
|
return false; |
|
} |
|
|
|
return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) || |
|
IsBlack()) && |
|
(!aTracingNeeded || |
|
HasNothingToTrace(static_cast<nsIDOMEventTarget*>(this))); |
|
} |
|
|
|
void |
|
nsGlobalWindow::UnmarkGrayTimers() |
|
{ |
|
for (nsTimeout* timeout = mTimeouts.getFirst(); |
|
timeout; |
|
timeout = timeout->getNext()) { |
|
if (timeout->mScriptHandler) { |
|
Function* f = timeout->mScriptHandler->GetCallback(); |
|
if (f) { |
|
// Callable() already does xpc_UnmarkGrayObject. |
|
DebugOnly<JS::Handle<JSObject*> > o = f->Callable(); |
|
MOZ_ASSERT(!JS::ObjectIsMarkedGray(o.value), |
|
"Should have been unmarked"); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//***************************************************************************** |
|
// nsGlobalWindow::nsIScriptGlobalObject |
|
//***************************************************************************** |
|
|
|
nsresult |
|
nsGlobalWindow::EnsureScriptEnvironment() |
|
{ |
|
nsGlobalWindow* outer = GetOuterWindowInternal(); |
|
if (!outer) { |
|
NS_WARNING("No outer window available!"); |
|
return NS_ERROR_FAILURE; |
|
} |
|
|
|
if (outer->GetWrapperPreserveColor()) { |
|
return NS_OK; |
|
} |
|
|
|
NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(), |
|
"No cached wrapper, but we have an inner window?"); |
|
|
|
// If this window is a [i]frame, don't bother GC'ing when the frame's context |
|
// is destroyed since a GC will happen when the frameset or host document is |
|
// destroyed anyway. |
|
nsCOMPtr<nsIScriptContext> context = new nsJSContext(!IsFrame(), outer); |
|
|
|
NS_ASSERTION(!outer->mContext, "Will overwrite mContext!"); |
|
|
|
// should probably assert the context is clean??? |
|
context->WillInitializeContext(); |
|
|
|
nsresult rv = context->InitContext(); |
|
NS_ENSURE_SUCCESS(rv, rv); |
|
|
|
outer->mContext = context; |
|
return NS_OK; |
|
} |
|
|
|
nsIScriptContext * |
|
nsGlobalWindow::GetScriptContext() |
|
{ |
|
nsGlobalWindow* outer = GetOuterWindowInternal(); |
|
return outer ? outer->mContext : nullptr; |
|
} |
|
|
|
JSObject * |
|
nsGlobalWindow::GetGlobalJSObject() |
|
{ |
|
return FastGetGlobalJSObject(); |
|
} |
|
|
|
void |
|
nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc) |
|
{ |
|
TraceWrapper(aTrc, "active window global"); |
|
} |
|
|
|
/* static */ |
|
JSObject* |
|
nsGlobalWindow::OuterObject(JSContext* aCx, JS::Handle<JSObject*> aObj) |
|
{ |
|
nsGlobalWindow* origWin = UnwrapDOMObject<nsGlobalWindow>(aObj); |
|
nsGlobalWindow* win = origWin->GetOuterWindowInternal(); |
|
|
|
if (!win) { |
|
// If we no longer have an outer window. No code should ever be |
|
// running on a window w/o an outer, which means this hook should |
|
// never be called when we have no outer. But just in case, return |
|
// null to prevent leaking an inner window to code in a different |
|
// window. |
|
NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!"); |
|
Throw(aCx, NS_ERROR_UNEXPECTED); |
|
return nullptr; |
|
} |
|
|
|
JS::Rooted<JSObject*> winObj(aCx, win->FastGetGlobalJSObject()); |
|
MOZ_ASSERT(winObj); |
|
|
|
// Note that while |wrapper| is same-compartment with cx, the outer window |
|
// might not be. If we're running script in an inactive scope and evalute |
|
// |this|, the outer window is actually a cross-compartment wrapper. So we |
|
// need to wrap here. |
|
if (!JS_WrapObject(aCx, &winObj)) { |
|
NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!"); |
|
Throw(aCx, NS_ERROR_UNEXPECTED); |
|
return nullptr; |
|
} |
|
|
|
return winObj; |
|
} |
|
|
|
bool |
|
nsGlobalWindow::WouldReuseInnerWindow(nsIDocument* aNewDocument) |
|
{ |
|
MOZ_ASSERT(IsOuterWindow()); |
|
|
|
// We reuse the inner window when: |
|
// a. We are currently at our original document. |
|
// b. At least one of the following conditions are true: |
|
// -- The new document is the same as the old document. This means that we're |
|
// getting called from document.open(). |
|
// -- The new document has the same origin as what we have loaded right now. |
|
|
|
if (!mDoc || !aNewDocument) { |
|
return false; |
|
} |
|
|
|
if (!mDoc->IsInitialDocument()) { |
|
return false; |
|
} |
|
|
|
NS_ASSERTION(NS_IsAboutBlank(mDoc->GetDocumentURI()), |
|
"How'd this happen?"); |
|
|
|
// Great, we're the original document, check for one of the other |
|
// conditions. |
|
|
|
if (mDoc == aNewDocument) { |
|
return true; |
|
} |
|
|
|
bool equal; |
|
if (NS_SUCCEEDED(mDoc->NodePrincipal()->Equals(aNewDocument->NodePrincipal(), |
|
&equal)) && |
|
equal) { |
|
// The origin is the same. |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void |
|
nsGlobalWindow::SetInitialPrincipalToSubject() |
|
{ |
|
MOZ_ASSERT(IsOuterWindow()); |
|
|
|
// First, grab the subject principal. |
|
nsCOMPtr<nsIPrincipal> newWindowPrincipal = nsContentUtils::SubjectPrincipal(); |
|
|
|
// Now, if we're about to use the system principal or an nsExpandedPrincipal, |
|
// make sure we're not using it for a content docshell. |
|
if (nsContentUtils::IsSystemOrExpandedPrincipal(newWindowPrincipal) && |
|
GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome) { |
|
newWindowPrincipal = nullptr; |
|
} |
|
|
|
// If there's an existing document, bail if it either: |
|
if (mDoc) { |
|
// (a) is not an initial about:blank document, or |
|
if (!mDoc->IsInitialDocument()) |
|
return; |
|
// (b) already has the correct principal. |
|
if (mDoc->NodePrincipal() == newWindowPrincipal) |
|
return; |
|
|
|
#ifdef DEBUG |
|
// If we have a document loaded at this point, it had better be about:blank. |
|
// Otherwise, something is really weird. |
|
nsCOMPtr<nsIURI> uri; |
|
mDoc->NodePrincipal()->GetURI(getter_AddRefs(uri)); |
|
NS_ASSERTION(uri && NS_IsAboutBlank(uri) && |
|
NS_IsAboutBlank(mDoc->GetDocumentURI()), |
|
"Unexpected original document"); |
|
#endif |
|
} |
|
|
|
GetDocShell()->CreateAboutBlankContentViewer(newWindowPrincipal); |
|
mDoc->SetIsInitialDocument(true); |
|
|
|
nsCOMPtr<nsIPresShell> shell = GetDocShell()->GetPresShell(); |
|
|
|
if (shell && !shell->DidInitialize()) { |
|
// Ensure that if someone plays with this document they will get |
|
// layout happening. |
|
nsRect r = shell->GetPresContext()->GetVisibleArea(); |
|
shell->Initialize(r.width, r.height); |
|
} |
|
} |
|
|
|
PopupControlState |
|
PushPopupControlState(PopupControlState aState, bool aForce) |
|
{ |
|
MOZ_ASSERT(NS_IsMainThread()); |
|
|
|
PopupControlState oldState = gPopupControlState; |
|
|
|
if (aState < gPopupControlState || aForce) { |
|
gPopupControlState = aState; |
|
} |
|
|
|
return oldState; |
|
} |
|
|
|
void |
|
PopPopupControlState(PopupControlState aState) |
|
{ |
|
MOZ_ASSERT(NS_IsMainThread()); |
|
|
|
gPopupControlState = aState; |
|
} |
|
|
|
PopupControlState |
|
nsGlobalWindow::PushPopupControlState(PopupControlState aState, |
|
bool aForce) const |
|
{ |
|
return ::PushPopupControlState(aState, aForce); |
|
} |
|
|
|
void |
|
nsGlobalWindow::PopPopupControlState(PopupControlState aState) const |
|
{ |
|
::PopPopupControlState(aState); |
|
} |
|
|
|
PopupControlState |
|
nsGlobalWindow::GetPopupControlState() const |
|
{ |
|
MOZ_ASSERT(NS_IsMainThread()); |
|
return gPopupControlState; |
|
} |
|
|
|
#define WINDOWSTATEHOLDER_IID \ |
|
{0x0b917c3e, 0xbd50, 0x4683, {0xaf, 0xc9, 0xc7, 0x81, 0x07, 0xae, 0x33, 0x26}} |
|
|
|
class WindowStateHolder final : public nsISupports |
|
{ |
|
public: |
|
NS_DECLARE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID) |
|
NS_DECL_ISUPPORTS |
|
|
|
WindowStateHolder(nsIScriptContext* aContext, nsGlobalWindow *aWindow); |
|
|
|
nsGlobalWindow* GetInnerWindow() { return mInnerWindow; } |
|
|
|
void DidRestoreWindow() |
|
{ |
|
mInnerWindow = nullptr; |
|
mInnerWindowReflector = nullptr; |
|
} |
|
|
|
protected: |
|
~WindowStateHolder(); |
|
|
|
nsGlobalWindow *mInnerWindow; |
|
// We hold onto this to make sure the inner window doesn't go away. The outer |
|
// window ends up recalculating it anyway. |
|
JS::PersistentRooted<JSObject*> mInnerWindowReflector; |
|
}; |
|
|
|
NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID) |
|
|
|
WindowStateHolder::WindowStateHolder(nsIScriptContext* aContext, |
|
nsGlobalWindow* aWindow) |
|
: mInnerWindow(aWindow), |
|
mInnerWindowReflector(aContext->GetNativeContext(), aWindow->GetWrapper()) |
|
{ |
|
NS_PRECONDITION(aWindow, "null window"); |
|
NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window"); |
|
|
|
aWindow->SuspendTimeouts(); |
|
|
|
// When a global goes into the bfcache, we disable script. |
|
xpc::Scriptability::Get(mInnerWindowReflector).SetDocShellAllowsScript(false); |
|
} |
|
|
|
WindowStateHolder::~WindowStateHolder() |
|
{ |
|
if (mInnerWindow) { |
|
// This window was left in the bfcache and is now going away. We need to |
|
// free it up. |
|
// Note that FreeInnerObjects may already have been called on the |
|
// inner window if its outer has already had SetDocShell(null) |
|
// called. |
|
mInnerWindow->FreeInnerObjects(); |
|
} |
|
} |
|
|
|
NS_IMPL_ISUPPORTS(WindowStateHolder, WindowStateHolder) |
|
|
|
// We need certain special behavior for remote XUL whitelisted domains, but we |
|
// don't want that behavior to take effect in automation, because we whitelist |
|
// all the mochitest domains. So we need to check a pref here. |
|
static bool |
|
TreatAsRemoteXUL(nsIPrincipal* aPrincipal) |
|
{ |
|
MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(aPrincipal)); |
|
return nsContentUtils::AllowXULXBLForPrincipal(aPrincipal) && |
|
!Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false); |
|
} |
|
|
|
static bool |
|
EnablePrivilege(JSContext* cx, unsigned argc, JS::Value* vp) |
|
{ |
|
Telemetry::Accumulate(Telemetry::ENABLE_PRIVILEGE_EVER_CALLED, true); |
|
return xpc::EnableUniversalXPConnect(cx); |
|
} |
|
|
|
static const JSFunctionSpec EnablePrivilegeSpec[] = { |
|
JS_FS("enablePrivilege", EnablePrivilege, 1, 0), |
|
JS_FS_END |
|
}; |
|
|
|
static bool |
|
InitializeLegacyNetscapeObject(JSContext* aCx, JS::Handle<JSObject*> aGlobal) |
|
{ |
|
JSAutoCompartment ac(aCx, aGlobal); |
|
|
|
// Note: MathJax depends on window.netscape being exposed. See bug 791526. |
|
JS::Rooted<JSObject*> obj(aCx); |
|
obj = JS_DefineObject(aCx, aGlobal, "netscape", nullptr); |
|
NS_ENSURE_TRUE(obj, false); |
|
|
|
obj = JS_DefineObject(aCx, obj, "security", nullptr); |
|
NS_ENSURE_TRUE(obj, false); |
|
|
|
// We hide enablePrivilege behind a pref because it has been altered in a |
|
// way that makes it fundamentally insecure to use in production. Mozilla |
|
// uses this pref during automated testing to support legacy test code that |
|
// uses enablePrivilege. If you're not doing test automation, you _must_ not |
|
// flip this pref, or you will be exposing all your users to security |
|
// vulnerabilities. |
|
if (!Preferences::GetBool("security.turn_off_all_security_so_that_viruses_can_take_over_this_computer")) { |
|
return true; |
|
} |
|
|
|
/* Define PrivilegeManager object with the necessary "static" methods. */ |
|
obj = JS_DefineObject(aCx, obj, "PrivilegeManager", nullptr); |
|
NS_ENSURE_TRUE(obj, false); |
|
|
|
return JS_DefineFunctions(aCx, obj, EnablePrivilegeSpec); |
|
} |
|
|
|
/** |
|
* Create a new global object that will be used for an inner window. |
|
* Return the native global and an nsISupports 'holder' that can be used |
|
* to manage the lifetime of it. |
|
*/ |
|
static nsresult |
|
CreateNativeGlobalForInner(JSContext* aCx, |
|
nsGlobalWindow* aNewInner, |
|
nsIURI* aURI, |
|
nsIPrincipal* aPrincipal, |
|
JS::MutableHandle<JSObject*> aGlobal) |
|
{ |
|
MOZ_ASSERT(aCx); |
|
MOZ_ASSERT(aNewInner); |
|
MOZ_ASSERT(aNewInner->IsInnerWindow()); |
|
MOZ_ASSERT(aPrincipal); |
|
|
|
// DOMWindow with nsEP is not supported, we have to make sure |
|
// no one creates one accidentally. |
|
nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(aPrincipal); |
|
MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported"); |
|
|
|
nsGlobalWindow *top = nullptr; |
|
if (aNewInner->GetOuterWindow()) { |
|
top = aNewInner->GetTopInternal(); |
|
} |
|
JS::CompartmentOptions options; |
|
|
|
// Sometimes add-ons load their own XUL windows, either as separate top-level |
|
// windows or inside a browser element. In such cases we want to tag the |
|
// window's compartment with the add-on ID. See bug 1092156. |
|
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { |
|
options.setAddonId(MapURIToAddonID(aURI)); |
|
} |
|
|
|
if (top) { |
|
if (top->GetGlobalJSObject()) { |
|
options.setSameZoneAs(top->GetGlobalJSObject()); |
|
} |
|
} |
|
|
|
// Determine if we need the Components object. |
|
bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) || |
|
TreatAsRemoteXUL(aPrincipal); |
|
uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT; |
|
flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK; |
|
|
|
if (!WindowBinding::Wrap(aCx, aNewInner, aNewInner, options, |
|
nsJSPrincipals::get(aPrincipal), false, aGlobal) || |
|
!xpc::InitGlobalObject(aCx, aGlobal, flags)) { |
|
return NS_ERROR_FAILURE; |
|
} |
|
|
|
MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal); |
|
|
|
// Set the location information for the new global, so that tools like |
|
// about:memory may use that information |
|
xpc::SetLocationForGlobal(aGlobal, aURI); |
|
|
|
if (!InitializeLegacyNetscapeObject(aCx, aGlobal)) { |
|
return NS_ERROR_FAILURE; |
|
} |
|
|
|
return NS_OK; |
|
} |
|
|
|
nsresult |
|
nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, |
|
nsISupports* aState, |
|
bool aForceReuseInnerWindow) |
|
{ |
|
NS_PRECONDITION(mDocumentPrincipal == nullptr, |
|
"mDocumentPrincipal prematurely set!"); |
|
MOZ_ASSERT(aDocument); |
|
|
|
if (IsInnerWindow()) { |
|
if (!mOuterWindow) { |
|
return NS_ERROR_NOT_INITIALIZED; |
|
} |
|
|
|
// Refuse to set a new document if the call came from an inner |
|
// window that's not the current inner window. |
|
if (mOuterWindow->GetCurrentInnerWindow() != this) { |
|
return NS_ERROR_NOT_AVAILABLE; |
|
} |
|
|
|
return GetOuterWindowInternal()->SetNewDocument(aDocument, aState, |
|
aForceReuseInnerWindow); |
|
} |
|
|
|
NS_PRECONDITION(IsOuterWindow(), "Must only be called on outer windows"); |
|
|
|
// Bail out early if we're in process of closing down the window. |
|
NS_ENSURE_STATE(!mCleanedUp); |
|
|
|
if (IsFrozen()) { |
|
// This outer is now getting its first inner, thaw the outer now |
|
// that it's ready and is getting an inner window. |
|
|
|
Thaw(); |
|
} |
|
|
|
NS_ASSERTION(!GetCurrentInnerWindow() || |
|
GetCurrentInnerWindow()->GetExtantDoc() == mDoc, |
|
"Uh, mDoc doesn't match the current inner window " |
|
"document!"); |
|
|
|
bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument); |
|
if (aForceReuseInnerWindow && |
|
!wouldReuseInnerWindow && |
|
mDoc && |
|
mDoc->NodePrincipal() != aDocument->NodePrincipal()) { |
|
NS_ERROR("Attempted forced inner window reuse while changing principal"); |
|
return NS_ERROR_UNEXPECTED; |
|
} |
|
|
|
nsCOMPtr<nsIDocument> oldDoc = mDoc; |
|
|
|
AutoJSAPI jsapi; |
|
jsapi.Init(); |
|
JSContext *cx = jsapi.cx(); |
|
|
|
// Check if we're anywhere near the stack limit before we reach the |
|
// transplanting code, since it has no good way to handle errors. This uses |
|
// the untrusted script limit, which is not strictly necessary since no |
|
// actual script should run. |
|
bool overrecursed = false; |
|
JS_CHECK_RECURSION_CONSERVATIVE_DONT_REPORT(cx, overrecursed = true); |
|
if (overrecursed) { |
|
NS_WARNING("Overrecursion in SetNewDocument"); |
|
return NS_ERROR_FAILURE; |
|
} |
|
|
|
if (!mDoc) { |
|
// First document load. |
|
|
|
// Get our private root. If it is equal to us, then we need to |
|
// attach our global key bindings that handles browser scrolling |
|
// and other browser commands. |
|
nsIDOMWindow* privateRoot = nsGlobalWindow::GetPrivateRoot(); |
|
|
|
if (privateRoot == static_cast<nsIDOMWindow*>(this)) { |
|
nsXBLService::AttachGlobalKeyHandler(mChromeEventHandler); |
|
} |
|
} |
|
|
|
/* No mDocShell means we're already been partially closed down. When that |
|
happens, setting status isn't a big requirement, so don't. (Doesn't happen |
|
under normal circumstances, but bug 49615 describes a case.) */ |
|
|
|
nsContentUtils::AddScriptRunner( |
|
NS_NewRunnableMethod(this, &nsGlobalWindow::ClearStatus)); |
|
|
|
// Sometimes, WouldReuseInnerWindow() returns true even if there's no inner |
|
// window (see bug 776497). Be safe. |
|
bool reUseInnerWindow = (aForceReuseInnerWindow || wouldReuseInnerWindow) && |
|
GetCurrentInnerWindowInternal(); |
|
|
|
nsresult rv = NS_OK; |
|
|
|
// Set mDoc even if this is an outer window to avoid |
|
// having to *always* reach into the inner window to find the |
|
// document. |
|
mDoc = aDocument; |
|
if (IsInnerWindow()) { |
|
ClearDocumentDependentSlots(cx); |
|
} |
|
|
|
// Take this opportunity to clear mSuspendedDoc. Our old inner window is now |
|
// responsible for unsuspending it. |
|
mSuspendedDoc = nullptr; |
|
|
|
#ifdef DEBUG |
|
mLastOpenedURI = aDocument->GetDocumentURI(); |
|
#endif |
|
|
|
mContext->WillInitializeContext(); |
|
|
|
nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); |
|
|
|
if (currentInner && currentInner->mNavigator) { |
|
currentInner->mNavigator->OnNavigation(); |
|
} |
|
|
|
nsRefPtr<nsGlobalWindow> newInnerWindow; |
|
bool createdInnerWindow = false; |
|
|
|
bool thisChrome = IsChromeWindow(); |
|
|
|
nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState); |
|
NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?"); |
|
|
|
bool handleDocumentOpen = false; |
|
|
|
JS::Rooted<JSObject*> newInnerGlobal(cx); |
|
if (reUseInnerWindow) { |
|
// We're reusing the current inner window. |
|
NS_ASSERTION(!currentInner->IsFrozen(), |
|
"We should never be reusing a shared inner window"); |
|
newInnerWindow = currentInner; |
|
newInnerGlobal = currentInner->GetWrapperPreserveColor(); |
|
|
|
if (aDocument != oldDoc) { |
|
JS::ExposeObjectToActiveJS(newInnerGlobal); |
|
} |
|
|
|
// We're reusing the inner window, but this still counts as a navigation, |
|
// so all expandos and such defined on the outer window should go away. Force |
|
// all Xray wrappers to be recomputed. |
|
JS::Rooted<JSObject*> rootedObject(cx, GetWrapperPreserveColor()); |
|
JS::ExposeObjectToActiveJS(rootedObject); |
|
if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) { |
|
return NS_ERROR_FAILURE; |
|
} |
|
|
|
// Inner windows are only reused for same-origin principals, but the principals |
|
// don't necessarily match exactly. Update the principal on the compartment to |
|
// match the new document. |
|
// NB: We don't just call currentInner->RefreshCompartmentPrincipals() here |
|
// because we haven't yet set its mDoc to aDocument. |
|
JSCompartment *compartment = js::GetObjectCompartment(newInnerGlobal); |
|
#ifdef DEBUG |
|
bool sameOrigin = false; |
|
nsIPrincipal *existing = |
|
nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment)); |
|
aDocument->NodePrincipal()->Equals(existing, &sameOrigin); |
|
MOZ_ASSERT(sameOrigin); |
|
#endif |
|
MOZ_ASSERT_IF(aDocument == oldDoc, |
|
xpc::GetCompartmentPrincipal(compartment) == |
|
aDocument->NodePrincipal()); |
|
if (aDocument != oldDoc) { |
|
JS_SetCompartmentPrincipals(compartment, |
|
nsJSPrincipals::get(aDocument->NodePrincipal())); |
|
// Make sure we clear out the old content XBL scope, so the new one will |
|
// get created with a principal that subsumes our new principal. |
|
xpc::ClearContentXBLScope(newInnerGlobal); |
|
} |
|
} else { |
|
if (aState) { |
|
newInnerWindow = wsh->GetInnerWindow(); |
|
newInnerGlobal = newInnerWindow->GetWrapperPreserveColor(); |
|
} else { |
|
if (thisChrome) { |
|
newInnerWindow = nsGlobalChromeWindow::Create(this); |
|
} else if (mIsModalContentWindow) { |
|
newInnerWindow = nsGlobalModalWindow::Create(this); |
|
} else { |
|
newInnerWindow |