Mirror of roytam1's UXP fork just in case Moonchild and Tobin decide to go after him
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.
 
 
 
 
 
 

15222 lines
453 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 "nsContentSecurityManager.h"
#include "nsScreen.h"
#include "nsHistory.h"
#include "nsDOMNavigationTiming.h"
#include "nsIDOMStorageManager.h"
#include "mozilla/dom/DOMStorage.h"
#include "mozilla/dom/IdleRequest.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/StorageEvent.h"
#include "mozilla/dom/StorageEventBinding.h"
#include "mozilla/dom/Timeout.h"
#include "mozilla/dom/TimeoutHandler.h"
#include "mozilla/IntegerPrintfMacros.h"
#if defined(MOZ_WIDGET_ANDROID)
#include "mozilla/dom/WindowOrientationObserver.h"
#endif
#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 "nsITimeoutHandler.h"
#include "nsIController.h"
#include "nsScriptNameSpaceManager.h"
#include "nsISlowScriptDebug.h"
#include "nsWindowMemoryReporter.h"
#include "WindowNamedPropertiesHandler.h"
#include "nsFrameSelection.h"
#include "nsNetUtil.h"
#include "nsVariant.h"
#include "nsPrintfCString.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/Sprintf.h"
#include "mozilla/Unused.h"
// 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 "mozilla/ThrottledEventQueue.h"
#include "AudioChannelService.h"
#include "nsAboutProtocolUtils.h"
#include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE
#include "PostMessageEvent.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/TabGroup.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 "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 "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 "nsIScreenManager.h"
#include "nsIEffectiveTLDService.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/IDBFactory.h"
#include "mozilla/dom/MessageChannel.h"
#include "mozilla/dom/Promise.h"
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/Gamepad.h"
#include "mozilla/dom/GamepadManager.h"
#endif
#include "nsRefreshDriver.h"
#include "Layers.h"
#include "mozilla/AddonPathService.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/Services.h"
#include "mozilla/dom/Location.h"
#include "nsHTMLDocument.h"
#include "nsWrapperCacheInlines.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "prrng.h"
#include "nsSandboxFlags.h"
#include "TimeChangeObserver.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"
#include "mozilla/dom/ImageBitmapBinding.h"
#include "mozilla/dom/ServiceWorkerRegistration.h"
#include "mozilla/dom/U2F.h"
#include "mozilla/dom/WebIDLGlobalNameHash.h"
#include "mozilla/dom/Worklet.h"
#ifdef HAVE_SIDEBAR
#include "mozilla/dom/ExternalBinding.h"
#endif
#ifdef MOZ_WEBSPEECH
#include "mozilla/dom/SpeechSynthesis.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
#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::PrincipalOriginAttributes;
using mozilla::TimeStamp;
using mozilla::TimeDuration;
using mozilla::dom::cache::CacheStorage;
static LazyLogModule gDOMLeakPRLog("DOMLeak");
nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr;
bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false;
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;
#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 {
// First apply any back pressure delay that might be in effect.
int32_t value = std::max(mBackPressureDelayMS, 0);
// Don't use the background timeout value when there are audio contexts
// present, so that baackground audio can keep running smoothly. (bug 1181073)
bool isBackground = mAudioContexts.IsEmpty() &&
(!mOuterWindow || mOuterWindow->IsBackground());
return
std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, value);
}
// 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
// The interval at which we execute idle callbacks
static uint32_t gThrottledIdlePeriodLength;
#define DEFAULT_THROTTLED_IDLE_PERIOD_LENGTH 10000
#define FORWARD_TO_OUTER(method, args, err_rval) \
PR_BEGIN_MACRO \
if (IsInnerWindow()) { \
nsGlobalWindow *outer = GetOuterWindowInternal(); \
if (!AsInner()->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(AsInner()->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 (!AsInner()->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 (!AsInner()->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 *)nsGlobalWindow::Cast(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 (!AsInner()->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<nsIDocument> kungFuDeathGrip = GetDoc(); \
::mozilla::Unused << kungFuDeathGrip; \
if (!mInnerWindow) { \
return err_rval; \
} \
} \
return GetCurrentInnerWindowInternal()->method args; \
} \
PR_END_MACRO
// CIDs
static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
#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_IMETHOD 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)
static already_AddRefed<nsIVariant>
CreateVoidVariant()
{
RefPtr<nsVariantCC> writable = new nsVariantCC();
writable->SetAsVoid();
return writable.forget();
}
nsresult
DialogValueHolder::Get(nsIPrincipal* aSubject, nsIVariant** aResult)
{
nsCOMPtr<nsIVariant> result;
if (aSubject->SubsumesConsideringDomain(mOrigin)) {
result = mValue;
} else {
result = CreateVoidVariant();
}
result.forget(aResult);
return NS_OK;
}
void
DialogValueHolder::Get(JSContext* aCx, JS::Handle<JSObject*> aScope,
nsIPrincipal* aSubject,
JS::MutableHandle<JS::Value> aResult,
mozilla::ErrorResult& aError)
{
if (aSubject->Subsumes(mOrigin)) {
aError = nsContentUtils::XPConnect()->VariantToJS(aCx, aScope,
mValue, aResult);
} else {
aResult.setUndefined();
}
}
class IdleRequestExecutor final : public nsIRunnable
, public nsICancelableRunnable
, public nsIIncrementalRunnable
{
public:
explicit IdleRequestExecutor(nsGlobalWindow* aWindow)
: mDispatched(false)
, mDeadline(TimeStamp::Now())
, mWindow(aWindow)
{
MOZ_DIAGNOSTIC_ASSERT(mWindow);
MOZ_DIAGNOSTIC_ASSERT(mWindow->IsInnerWindow());
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
NS_DECL_NSIRUNNABLE
nsresult Cancel() override;
void SetDeadline(TimeStamp aDeadline) override;
void MaybeDispatch();
private:
~IdleRequestExecutor() {}
bool mDispatched;
TimeStamp mDeadline;
RefPtr<nsGlobalWindow> mWindow;
};
NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequestExecutor)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
IdleRequestExecutor::Run()
{
MOZ_ASSERT(NS_IsMainThread());
mDispatched = false;
if (mWindow) {
return mWindow->ExecuteIdleRequest(mDeadline);
}
return NS_OK;
}
nsresult
IdleRequestExecutor::Cancel()
{
MOZ_ASSERT(NS_IsMainThread());
mWindow = nullptr;
return NS_OK;
}
void
IdleRequestExecutor::SetDeadline(TimeStamp aDeadline)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mWindow) {
return;
}
mDeadline = aDeadline;
}
void
IdleRequestExecutor::MaybeDispatch()
{
// If we've already dispatched the executor we don't want to do it
// again. Also, if we've called IdleRequestExecutor::Cancel mWindow
// will be null, which indicates that we shouldn't dispatch this
// executor either.
if (mDispatched || !mWindow) {
return;
}
mDispatched = true;
RefPtr<IdleRequestExecutor> request = this;
NS_IdleDispatchToCurrentThread(request.forget());
}
class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler
{
public:
explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
: mExecutor(aExecutor)
{
}
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestExecutorTimeoutHandler,
TimeoutHandler)
nsresult Call() override
{
mExecutor->MaybeDispatch();
return NS_OK;
}
private:
~IdleRequestExecutorTimeoutHandler() {}
RefPtr<IdleRequestExecutor> mExecutor;
};
NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler, mExecutor)
NS_IMPL_ADDREF_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
NS_IMPL_RELEASE_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
void
nsGlobalWindow::ScheduleIdleRequestDispatch()
{
AssertIsOnMainThread();
if (mIdleRequestCallbacks.isEmpty()) {
if (mIdleRequestExecutor) {
mIdleRequestExecutor->Cancel();
mIdleRequestExecutor = nullptr;
}
return;
}
if (!mIdleRequestExecutor) {
mIdleRequestExecutor = new IdleRequestExecutor(this);
}
nsPIDOMWindowOuter* outer = GetOuterWindow();
if (outer && outer->AsOuter()->IsBackground()) {
nsCOMPtr<nsITimeoutHandler> handler = new IdleRequestExecutorTimeoutHandler(mIdleRequestExecutor);
int32_t dummy;
// Set a timeout handler with a timeout of 0 ms to throttle idle
// callback requests coming from a backround window using
// background timeout throttling.
SetTimeoutOrInterval(handler, 0, false,
Timeout::Reason::eIdleCallbackTimeout, &dummy);
return;
}
mIdleRequestExecutor->MaybeDispatch();
}
void
nsGlobalWindow::SuspendIdleRequests()
{
if (mIdleRequestExecutor) {
mIdleRequestExecutor->Cancel();
mIdleRequestExecutor = nullptr;
}
}
void
nsGlobalWindow::ResumeIdleRequests()
{
MOZ_ASSERT(!mIdleRequestExecutor);
ScheduleIdleRequestDispatch();
}
void
nsGlobalWindow::InsertIdleCallback(IdleRequest* aRequest)
{
AssertIsOnMainThread();
mIdleRequestCallbacks.insertBack(aRequest);
aRequest->AddRef();
}
void
nsGlobalWindow::RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest)
{
AssertIsOnMainThread();
if (aRequest->HasTimeout()) {
ClearTimeoutOrInterval(aRequest->GetTimeoutHandle(),
Timeout::Reason::eIdleCallbackTimeout);
}
aRequest->removeFrom(mIdleRequestCallbacks);
aRequest->Release();
}
nsresult
nsGlobalWindow::RunIdleRequest(IdleRequest* aRequest,
DOMHighResTimeStamp aDeadline,
bool aDidTimeout)
{
AssertIsOnMainThread();
RefPtr<IdleRequest> request(aRequest);
RemoveIdleCallback(request);
return request->IdleRun(AsInner(), aDeadline, aDidTimeout);
}
nsresult
nsGlobalWindow::ExecuteIdleRequest(TimeStamp aDeadline)
{
AssertIsOnMainThread();
RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
if (!request) {
// There are no more idle requests, so stop scheduling idle
// request callbacks.
return NS_OK;
}
DOMHighResTimeStamp deadline = 0.0;
if (Performance* perf = GetPerformance()) {
deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
}
nsresult result = RunIdleRequest(request, deadline, false);
ScheduleIdleRequestDispatch();
return result;
}
class IdleRequestTimeoutHandler final : public TimeoutHandler
{
public:
IdleRequestTimeoutHandler(JSContext* aCx,
IdleRequest* aIdleRequest,
nsPIDOMWindowInner* aWindow)
: TimeoutHandler(aCx)
, mIdleRequest(aIdleRequest)
, mWindow(aWindow)
{
}
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestTimeoutHandler,
TimeoutHandler)
nsresult Call() override
{
return nsGlobalWindow::Cast(mWindow)->RunIdleRequest(mIdleRequest, 0.0, true);
}
private:
~IdleRequestTimeoutHandler() {}
RefPtr<IdleRequest> mIdleRequest;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
};
NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestTimeoutHandler,
TimeoutHandler,
mIdleRequest,
mWindow)
NS_IMPL_ADDREF_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
NS_IMPL_RELEASE_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
uint32_t
nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
IdleRequestCallback& aCallback,
const IdleRequestOptions& aOptions,
ErrorResult& aError)
{
MOZ_RELEASE_ASSERT(IsInnerWindow());
AssertIsOnMainThread();
uint32_t handle = mIdleRequestCallbackCounter++;
RefPtr<IdleRequest> request =
new IdleRequest(&aCallback, handle);
if (aOptions.mTimeout.WasPassed()) {
int32_t timeoutHandle;
nsCOMPtr<nsITimeoutHandler> handler(new IdleRequestTimeoutHandler(aCx, request, AsInner()));
nsresult rv = SetTimeoutOrInterval(
handler, aOptions.mTimeout.Value(), false,
Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
if (NS_WARN_IF(NS_FAILED(rv))) {
return 0;
}
request->SetTimeoutHandle(timeoutHandle);
}
// If the list of idle callback requests is not empty it means that
// we've already dispatched the first idle request. If we're
// suspended we should only queue the idle callback and not schedule
// it to run, that will be done in ResumeIdleRequest.
bool needsScheduling = !IsSuspended() && mIdleRequestCallbacks.isEmpty();
// mIdleRequestCallbacks now owns request
InsertIdleCallback(request);
if (needsScheduling) {
ScheduleIdleRequestDispatch();
}
return handle;
}
void
nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
{
MOZ_RELEASE_ASSERT(IsInnerWindow());
for (IdleRequest* r : mIdleRequestCallbacks) {
if (r->Handle() == aHandle) {
RemoveIdleCallback(r);
break;
}
}
}
void
nsGlobalWindow::DisableIdleCallbackRequests()
{
if (mIdleRequestExecutor) {
mIdleRequestExecutor->Cancel();
mIdleRequestExecutor = nullptr;
}
while (!mIdleRequestCallbacks.isEmpty()) {
RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
RemoveIdleCallback(request);
}
}
namespace mozilla {
namespace dom {
extern uint64_t
NextWindowID();
} // namespace dom
} // namespace mozilla
template<class T>
nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMWindowOuter *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),
mInnerObjectsFreed(false),
mIsModalContentWindow(false),
mIsActive(false), mIsBackground(false),
mMediaSuspend(Preferences::GetBool("media.block-autoplay-until-in-foreground", true) ?
nsISuspendedTypes::SUSPENDED_BLOCK : nsISuspendedTypes::NONE_SUSPENDED),
mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
mDesktopModeViewport(false), mIsRootOuterWindow(false), mInnerWindow(nullptr),
mOuterWindow(aOuterWindow),
// Make sure no actual window ends up with mWindowID == 0
mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false)
{}
template<class T>
nsPIDOMWindow<T>::~nsPIDOMWindow() {}
/* static */
nsPIDOMWindowOuter*
nsPIDOMWindowOuter::GetFromCurrentInner(nsPIDOMWindowInner* aInner)
{
if (!aInner) {
return nullptr;
}
nsPIDOMWindowOuter* outer = aInner->GetOuterWindow();
if (!outer || outer->GetCurrentInnerWindow() != aInner) {
return nullptr;
}
return outer;
}
// 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:
constexpr nsOuterWindowProxy() : js::Wrapper(0) { }
virtual bool finalizeInBackground(const 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<JS::PropertyDescriptor> desc)
const override;
virtual bool defineProperty(JSContext* cx,
JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id,
JS::Handle<JS::PropertyDescriptor> 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 &result) const override;
virtual bool getPrototypeIfOrdinary(JSContext* cx,
JS::Handle<JSObject*> proxy,
bool* isOrdinary,
JS::MutableHandle<JSObject*> protop) 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<JS::PropertyDescriptor> 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* GetOuterWindow(JSObject *proxy)
{
nsGlobalWindow* outerWindow = nsGlobalWindow::FromSupports(
static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
MOZ_ASSERT_IF(outerWindow, outerWindow->IsOuterWindow());
return outerWindow;
}
// 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<nsPIDOMWindowOuter>
GetSubframeWindow(JSContext *cx,
JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id) const;
bool AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
JS::AutoIdVector &props) const;
};
static const js::ClassExtension OuterWindowProxyClassExtension = PROXY_MAKE_EXT(
nsOuterWindowProxy::ObjectMoved
);
const js::Class OuterWindowProxyClass = PROXY_CLASS_WITH_EXT(
"Proxy",
0, /* additional class flags */
&OuterWindowProxyClassExtension);
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* outerWindow = GetOuterWindow(proxy);
if (outerWindow) {
outerWindow->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.
outerWindow->PoisonOuterWindowProxy(proxy);
}
}
bool
nsOuterWindowProxy::getPropertyDescriptor(JSContext* cx,
JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id,
JS::MutableHandle<JS::PropertyDescriptor> 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<JS::PropertyDescriptor> 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
// When we change this to always claim the property is configurable (bug
// 1178639), update the comments in nsOuterWindowProxy::defineProperty
// accordingly.
return js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
}
bool
nsOuterWindowProxy::defineProperty(JSContext* cx,
JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id,
JS::Handle<JS::PropertyDescriptor> desc,
JS::ObjectOpResult &result) const
{
if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
// 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();
}
#ifndef RELEASE_OR_BETA // To be turned on in bug 1178638.
// 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 (desc.hasConfigurable() && !desc.configurable() &&
!nsContentUtils::IsCallerChrome()) {
return ThrowErrorMessage(cx, MSG_DEFINE_NON_CONFIGURABLE_PROP_ON_WINDOW);
}
// Note that if hasConfigurable() is false we do NOT want to
// setConfigurable(true). That would make this code:
//
// var x;
// window.x = 5;
//
// fail, because the JS engine ends up converting the assignment into a define
// with !hasConfigurable(), but the var actually declared a non-configurable
// property on our underlying Window object, so the set would fail if we
// forced setConfigurable(true) here. What we want to do instead is change
// getOwnPropertyDescriptor to always claim configurable. See bug 1178639.
#endif
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<nsPIDOMWindowOuter> frame = GetSubframeWindow(cx, proxy, id)) {
// Fail (which means throw if strict, else return false).
return result.failCantDeleteWindowElement();
}
if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
// Indexed, but not supported. Spec says return true.
return result.succeed();
}
return js::Wrapper::delete_(cx, proxy, id, result);
}
bool
nsOuterWindowProxy::getPrototypeIfOrdinary(JSContext* cx,
JS::Handle<JSObject*> proxy,
bool* isOrdinary,
JS::MutableHandle<JSObject*> protop) const
{
// Window's [[GetPrototypeOf]] trap isn't the ordinary definition:
//
// https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof
//
// We nonetheless can implement it with a static [[Prototype]], because
// wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp) supply
// all non-ordinary behavior.
//
// But from a spec point of view, it's the exact same object in both cases --
// only the observer's changed. So this getPrototypeIfOrdinary trap on the
// non-wrapper object *must* report non-ordinary, even if static [[Prototype]]
// usually means ordinary.
*isOrdinary = false;
return true;
}
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<nsPIDOMWindowOuter> 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<nsPIDOMWindowOuter> 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
{
if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
// 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<nsPIDOMWindowOuter> frame = GetSubframeWindow(cx, proxy, id);
if (!frame) {
found = false;
return true;
}
found = true;
// Just return the window's global
nsGlobalWindow* global = nsGlobalWindow::Cast(frame);
frame->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);
}
JS::ExposeObjectToActiveJS(obj);
vp.setObject(*obj);
return JS_WrapValue(cx, vp);
}
already_AddRefed<nsPIDOMWindowOuter>
nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id) const
{
uint32_t index = GetArrayIndexFromId(cx, id);
if (!IsArrayIndex(index)) {
return nullptr;
}
nsGlobalWindow* win = GetOuterWindow(proxy);
MOZ_ASSERT(win->IsOuterWindow());
return win->IndexedGetterOuter(index);
}
bool
nsOuterWindowProxy::AppendIndexedPropertyNames(JSContext *cx, JSObject *proxy,
JS::AutoIdVector &props) const
{
uint32_t length = GetOuterWindow(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) {
if (!props.append(INT_TO_JSID(i))) {
return false;
}
}
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* outerWindow = GetOuterWindow(obj);
if (outerWindow) {
outerWindow->UpdateWrapper(obj, old);
}
}
const nsOuterWindowProxy
nsOuterWindowProxy::singleton;
class nsChromeOuterWindowProxy : public nsOuterWindowProxy
{
public:
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);
MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
return obj;
}
namespace {
// The maximum number of timer callbacks we will try to run in a single event
// loop runnable.
#define DEFAULT_TARGET_MAX_CONSECUTIVE_CALLBACKS 5
uint32_t gTargetMaxConsecutiveCallbacks;
// The number of queued runnables within the TabGroup ThrottledEventQueue
// at which to begin applying back pressure to the window.
#define DEFAULT_THROTTLED_EVENT_QUEUE_BACK_PRESSURE 5000
static uint32_t gThrottledEventQueueBackPressure;
// The amount of delay to apply to timers when back pressure is triggered.
// As the length of the ThrottledEventQueue grows delay is increased. The
// delay is scaled such that every kThrottledEventQueueBackPressure runnables
// in the queue equates to an additional kBackPressureDelayMS.
#define DEFAULT_BACK_PRESSURE_DELAY_MS 250
static uint32_t gBackPressureDelayMS;
// This defines a limit for how much the delay must drop before we actually
// reduce back pressure throttle amount. This makes the throttle delay
// a bit "sticky" once we enter back pressure.
#define DEFAULT_BACK_PRESSURE_DELAY_REDUCTION_THRESHOLD_MS 1000
static uint32_t gBackPressureDelayReductionThresholdMS;
// The minimum delay we can reduce back pressure to before we just floor
// the value back to zero. This allows us to ensure that we can exit
// back pressure event if there are always a small number of runnables
// queued up.
#define DEFAULT_BACK_PRESSURE_DELAY_MINIMUM_MS 100
static uint32_t gBackPressureDelayMinimumMS;
} // anonymous namespace
//*****************************************************************************
//*** nsGlobalWindow: Object Management
//*****************************************************************************
nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
: nsPIDOMWindow<nsISupports>(aOuterWindow ? aOuterWindow->AsOuter() : nullptr),
mIdleFuzzFactor(0),
mIdleCallbackIndex(-1),
mCurrentlyIdle(false),
mAddActiveEventFuzzTime(true),
mFullScreen(false),
mFullscreenMode(false),
mIsClosed(false),
mInClose(false),
mHavePendingClose(false),
mHadOriginalOpener(false),
mOriginalOpenerWasSecureContext(false),
mIsPopupSpam(false),
mBlockScriptedClosingFlag(false),
mWasOffline(false),
mNotifyIdleObserversIdleOnThaw(false),
mNotifyIdleObserversActiveOnThaw(false),
mCreatingInnerWindow(false),
mIsChrome(false),
mCleanMessageManager(false),
mNeedsFocus(true),
mHasFocus(false),
mShowFocusRingForContent(false),
mFocusByKeyOccurred(false),
mHasGamepad(false),
#ifdef MOZ_GAMEPAD
mHasSeenGamepadInput(false),
#endif
mNotifiedIDDestroyed(false),
mAllowScriptsToClose(false),
mTimeoutInsertionPoint(nullptr),
mTimeoutIdCounter(1),
mTimeoutFiringDepth(0),
mSuspendDepth(0),
mFreezeDepth(0),
mBackPressureDelayMS(0),
mFocusMethod(0),
mSerial(0),
mIdleCallbackTimeoutCounter(1),
mIdleRequestCallbackCounter(1),
mIdleRequestExecutor(nullptr),
#ifdef DEBUG
mSetOpenerWindowCalled(false),
#endif
mCleanedUp(false),
mDialogAbuseCount(0),
mAreDialogsEnabled(true),
#ifdef DEBUG
mIsValidatingTabGroup(false),
#endif
mCanSkipCCGeneration(0)
{
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.
MOZ_ASSERT(IsFrozen());
}
// 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++;
static bool sFirstTime = true;
if (sFirstTime) {
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);
Preferences::AddUintVarCache(&gThrottledIdlePeriodLength,
"dom.idle_period.throttled_length",
DEFAULT_THROTTLED_IDLE_PERIOD_LENGTH);
Preferences::AddUintVarCache(&gThrottledEventQueueBackPressure,
"dom.timeout.throttled_event_queue_back_pressure",
DEFAULT_THROTTLED_EVENT_QUEUE_BACK_PRESSURE);
Preferences::AddUintVarCache(&gBackPressureDelayMS,
"dom.timeout.back_pressure_delay_ms",
DEFAULT_BACK_PRESSURE_DELAY_MS);
Preferences::AddUintVarCache(&gBackPressureDelayReductionThresholdMS,
"dom.timeout.back_pressure_delay_reduction_threshold_ms",
DEFAULT_BACK_PRESSURE_DELAY_REDUCTION_THRESHOLD_MS);
Preferences::AddUintVarCache(&gBackPressureDelayMinimumMS,
"dom.timeout.back_pressure_delay_minimum_ms",
DEFAULT_BACK_PRESSURE_DELAY_MINIMUM_MS);
Preferences::AddUintVarCache(&gTargetMaxConsecutiveCallbacks,
"dom.timeout.max_consecutive_callbacks",
DEFAULT_TARGET_MAX_CONSECUTIVE_CALLBACKS);
sFirstTime = 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();
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) {
url = mLastOpenedURI->GetSpecOrDefault();
// 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 = nsGlobalWindow::Cast(mOuterWindow);
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 {
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);
}
}
// We don't have to leave the tab group if we are an inner window.
if (mTabGroup && IsOuterWindow()) {
mTabGroup->Leave(AsOuter());
}
// 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()) {
RefPtr<DOMEventTargetHelper> target = iter.Get()->GetKey();
target->DisconnectFromOwner();
}
mEventTargetObjects.Clear();
}
// static
void
nsGlobalWindow::ShutDown()
{
AssertIsOnMainThread();
if (gDumpFile && gDumpFile != stdout) {
fclose(gDumpFile);
}
gDumpFile = nullptr;
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()) {
SetIsPopupSpamWindow(false);
}
}
void
nsGlobalWindow::SetIsPopupSpamWindow(bool aIsPopupSpam)
{
MOZ_ASSERT(IsOuterWindow());
mIsPopupSpam = aIsPopupSpam;
if (aIsPopupSpam) {
++gOpenPopupSpamCount;
} else {
--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");
}
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;
mCustomElements = nullptr;
mFrames = nullptr;
mWindowUtils = nullptr;
mApplicationCache = nullptr;
mIndexedDB = nullptr;
mConsole = nullptr;
mExternal = nullptr;
mMozSelfSupport = nullptr;
mPerformance = nullptr;
#ifdef MOZ_WEBSPEECH
mSpeechSynthesis = nullptr;
#endif
#if defined(MOZ_WIDGET_ANDROID)
mOrientationChangeObserver = 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;
DisableIdleCallbackRequests();
} 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;
}
mServiceWorkerRegistrationTable.Clear();
}
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);
if (auto* reporter = nsWindowMemoryReporter::Get()) {
reporter->ObserveDOMWindowDetached(this);
}
mInnerObjectsFreed = true;
// Kill all of the workers for this window.
mozilla::dom::workers::CancelWorkersForWindow(AsInner());
ClearAllTimeouts();
if (mIdleTimer) {
mIdleTimer->Cancel();
mIdleTimer = nullptr;
}
mIdleObservers.Clear();
DisableIdleCallbackRequests();
mChromeEventHandler = nullptr;
if (mListenerManager) {
mListenerManager->Disconnect();
mListenerManager = nullptr;
}
mLocation = nullptr;
mHistory = nullptr;
mCustomElements = nullptr;
if (mNavigator) {
mNavigator->OnNavigation();
mNavigator->Invalidate();
mNavigator = nullptr;
}
if (mScreen) {
mScreen = nullptr;
}
#if defined(MOZ_WIDGET_ANDROID)
mOrientationChangeObserver = nullptr;
#endif
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;
UnlinkHostObjectURIs();
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)
if (aIID.Equals(NS_GET_IID(nsPIDOMWindowInner))) {
foundInterface = AsInner();
} else
if (aIID.Equals(NS_GET_IID(mozIDOMWindow)) && IsInnerWindow()) {
foundInterface = AsInner();
} else
if (aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter))) {
foundInterface = AsOuter();
} else
if (aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) && IsOuterWindow()) {
foundInterface = AsOuter();
} else
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)
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) {
for (auto iter = tmp->mCachedXBLPrototypeHandlers->Iter();
!iter.Done();
iter.Next()) {
iter.Data().exposeToActiveJS();
}
}
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()) {
uri = tmp->mDoc->GetDocumentURI()->GetSpecOrDefault();
}
SprintfLiteral(name, "nsGlobalWindow # %" PRIu64 " %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)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerRegistrationTable)
#ifdef MOZ_WEBSPEECH
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
for (Timeout* timeout = tmp->mTimeouts.getFirst();
timeout;
timeout = timeout->getNext()) {
cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
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(mIdleRequestExecutor)
for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
#ifdef MOZ_GAMEPAD
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
// 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(mU2F)
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)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorkerRegistrationTable)
#ifdef MOZ_WEBSPEECH
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
#endif
if (tmp->mOuterWindow) {
nsGlobalWindow::Cast(tmp->mOuterWindow)->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(mCustomElements)
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)
// 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(mU2F)
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(mIdleRequestExecutor)
tmp->DisableIdleCallbackRequests();
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
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow)
if (tmp->mCachedXBLPrototypeHandlers) {
for (auto iter = tmp->mCachedXBLPrototypeHandlers->Iter();
!iter.Done();
iter.Next()) {
aCallbacks.Trace(&iter.Data(), "Cached XBL prototype handler", aClosure);
}
}
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 (Timeout* timeout = mTimeouts.getFirst();
timeout;
timeout = timeout->getNext()) {
if (timeout->mScriptHandler) {
timeout->mScriptHandler->MarkForCC();
}
}
}
//*****************************************************************************
// 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();
if (!outer) {
return nullptr;
}
return outer->mContext;
}
JSObject *
nsGlobalWindow::GetGlobalJSObject()
{
return FastGetGlobalJSObject();
}
void
nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc)
{
TraceWrapper(aTrc, "active window global");
}
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;
}
#ifdef DEBUG
{
nsCOMPtr<nsIURI> uri;
mDoc->GetDocumentURI()->CloneIgnoringRef(getter_AddRefs(uri));
NS_ASSERTION(NS_IsAboutBlank(uri), "How'd this happen?");
}
#endif
// 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::SubjectPrincipalOrSystemIfNativeCaller();
// We should never create windows with an expanded principal.
// If we have a system principal, make sure we're not using it for a content
// docshell.
// NOTE: Please keep this logic in sync with nsWebShellWindow::Initialize().
if (nsContentUtils::IsExpandedPrincipal(newWindowPrincipal) ||
(nsContentUtils::IsSystemPrincipal(newWindowPrincipal) &&
GetDocShell()->ItemType() != nsIDocShellTreeItem::typeChrome)) {
newWindowPrincipal = nullptr;
}