diff options
Diffstat (limited to 'js/xpconnect/src/xpcpublic.h')
-rw-r--r-- | js/xpconnect/src/xpcpublic.h | 635 |
1 files changed, 635 insertions, 0 deletions
diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h new file mode 100644 index 0000000000..fc8670d460 --- /dev/null +++ b/js/xpconnect/src/xpcpublic.h @@ -0,0 +1,635 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ +/* 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/. */ + +#ifndef xpcpublic_h +#define xpcpublic_h + +#include "jsapi.h" +#include "js/HeapAPI.h" +#include "js/GCAPI.h" +#include "js/Proxy.h" + +#include "nsISupports.h" +#include "nsIURI.h" +#include "nsIPrincipal.h" +#include "nsIGlobalObject.h" +#include "nsPIDOMWindow.h" +#include "nsWrapperCache.h" +#include "nsStringGlue.h" +#include "nsTArray.h" +#include "mozilla/dom/JSSlots.h" +#include "mozilla/fallible.h" +#include "nsMathUtils.h" +#include "nsStringBuffer.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/Preferences.h" + +class nsGlobalWindow; +class nsIPrincipal; +class nsScriptNameSpaceManager; +class nsIMemoryReporterCallback; + +namespace mozilla { +namespace dom { +class Exception; +} +} + +typedef void (* xpcGCCallback)(JSGCStatus status); + +namespace xpc { + +class Scriptability { +public: + explicit Scriptability(JSCompartment* c); + bool Allowed(); + bool IsImmuneToScriptPolicy(); + + void Block(); + void Unblock(); + void SetDocShellAllowsScript(bool aAllowed); + + static Scriptability& Get(JSObject* aScope); + +private: + // Whenever a consumer wishes to prevent script from running on a global, + // it increments this value with a call to Block(). When it wishes to + // re-enable it (if ever), it decrements this value with a call to Unblock(). + // Script may not run if this value is non-zero. + uint32_t mScriptBlocks; + + // Whether the docshell allows javascript in this scope. If this scope + // doesn't have a docshell, this value is always true. + bool mDocShellAllowsScript; + + // Whether this scope is immune to user-defined or addon-defined script + // policy. + bool mImmuneToScriptPolicy; + + // Whether the new-style domain policy when this compartment was created + // forbids script execution. + bool mScriptBlockedByPolicy; +}; + +JSObject* +TransplantObject(JSContext* cx, JS::HandleObject origobj, JS::HandleObject target); + +bool IsContentXBLScope(JSCompartment* compartment); +bool IsInContentXBLScope(JSObject* obj); + +// Return a raw XBL scope object corresponding to contentScope, which must +// be an object whose global is a DOM window. +// +// The return value is not wrapped into cx->compartment, so be sure to enter +// its compartment before doing anything meaningful. +// +// Also note that XBL scopes are lazily created, so the return-value should be +// null-checked unless the caller can ensure that the scope must already +// exist. +// +// This function asserts if |contentScope| is itself in an XBL scope to catch +// sloppy consumers. Conversely, GetXBLScopeOrGlobal will handle objects that +// are in XBL scope (by just returning the global). +JSObject* +GetXBLScope(JSContext* cx, JSObject* contentScope); + +inline JSObject* +GetXBLScopeOrGlobal(JSContext* cx, JSObject* obj) +{ + if (IsInContentXBLScope(obj)) + return js::GetGlobalForObjectCrossCompartment(obj); + return GetXBLScope(cx, obj); +} + +// This function is similar to GetXBLScopeOrGlobal. However, if |obj| is a +// chrome scope, then it will return an add-on scope if addonId is non-null. +// Like GetXBLScopeOrGlobal, it returns the scope of |obj| if it's already a +// content XBL scope. But it asserts that |obj| is not an add-on scope. +JSObject* +GetScopeForXBLExecution(JSContext* cx, JS::HandleObject obj, JSAddonId* addonId); + +// Returns whether XBL scopes have been explicitly disabled for code running +// in this compartment. See the comment around mAllowContentXBLScope. +bool +AllowContentXBLScope(JSCompartment* c); + +// Returns whether we will use an XBL scope for this compartment. This is +// semantically equivalent to comparing global != GetXBLScope(global), but it +// does not have the side-effect of eagerly creating the XBL scope if it does +// not already exist. +bool +UseContentXBLScope(JSCompartment* c); + +// Clear out the content XBL scope (if any) on the given global. This will +// force creation of a new one if one is needed again. +void +ClearContentXBLScope(JSObject* global); + +bool +IsInAddonScope(JSObject* obj); + +JSObject* +GetAddonScope(JSContext* cx, JS::HandleObject contentScope, JSAddonId* addonId); + +bool +IsSandboxPrototypeProxy(JSObject* obj); + +bool +IsReflector(JSObject* obj); + +bool +IsXrayWrapper(JSObject* obj); + +// If this function was created for a given XrayWrapper, returns the global of +// the Xrayed object. Otherwise, returns the global of the function. +// +// To emphasize the obvious: the return value here is not necessarily same- +// compartment with the argument. +JSObject* +XrayAwareCalleeGlobal(JSObject* fun); + +void +TraceXPCGlobal(JSTracer* trc, JSObject* obj); + +} /* namespace xpc */ + +namespace JS { + +struct RuntimeStats; + +} // namespace JS + +#define XPC_WRAPPER_FLAGS (JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE) + +#define XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(n) \ + JSCLASS_DOM_GLOBAL | JSCLASS_HAS_PRIVATE | \ + JSCLASS_PRIVATE_IS_NSISUPPORTS | \ + JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS + n) + +#define XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET (JSCLASS_GLOBAL_SLOT_COUNT + DOM_GLOBAL_SLOTS) + +#define XPCONNECT_GLOBAL_FLAGS XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(0) + +inline JSObject* +xpc_FastGetCachedWrapper(JSContext* cx, nsWrapperCache* cache, JS::MutableHandleValue vp) +{ + if (cache) { + JSObject* wrapper = cache->GetWrapper(); + if (wrapper && + js::GetObjectCompartment(wrapper) == js::GetContextCompartment(cx)) + { + vp.setObject(*wrapper); + return wrapper; + } + } + + return nullptr; +} + +// If aVariant is an XPCVariant, this marks the object to be in aGeneration. +// This also unmarks the gray JSObject. +extern void +xpc_MarkInCCGeneration(nsISupports* aVariant, uint32_t aGeneration); + +// If aWrappedJS is a JS wrapper, unmark its JSObject. +extern void +xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS); + +extern void +xpc_UnmarkSkippableJSHolders(); + +// readable string conversions, static methods and members only +class XPCStringConvert +{ + // One-slot cache, because it turns out it's common for web pages to + // get the same string a few times in a row. We get about a 40% cache + // hit rate on this cache last it was measured. We'd get about 70% + // hit rate with a hashtable with removal on finalization, but that + // would take a lot more machinery. + struct ZoneStringCache + { + // mString owns mBuffer. mString is a JS thing, so it can only die + // during GC, though it can drop its ref to the buffer if it gets + // flattened and wasn't null-terminated. We clear mString and mBuffer + // during GC and in our finalizer (to catch the flatterning case). As + // long as the above holds, mBuffer should not be a dangling pointer, so + // using this as a cache key should be safe. + // + // We also need to include the string's length in the cache key, because + // now that we allow non-null-terminated buffers we can have two strings + // with the same mBuffer but different lengths. + void* mBuffer = nullptr; + uint32_t mLength = 0; + JSString* mString = nullptr; + }; + +public: + + // If the string shares the readable's buffer, that buffer will + // get assigned to *sharedBuffer. Otherwise null will be + // assigned. + static bool ReadableToJSVal(JSContext* cx, const nsAString& readable, + nsStringBuffer** sharedBuffer, + JS::MutableHandleValue vp); + + // Convert the given stringbuffer/length pair to a jsval + static MOZ_ALWAYS_INLINE bool + StringBufferToJSVal(JSContext* cx, nsStringBuffer* buf, uint32_t length, + JS::MutableHandleValue rval, bool* sharedBuffer) + { + JS::Zone* zone = js::GetContextZone(cx); + ZoneStringCache* cache = static_cast<ZoneStringCache*>(JS_GetZoneUserData(zone)); + if (cache && buf == cache->mBuffer && length == cache->mLength) { + MOZ_ASSERT(JS::GetStringZone(cache->mString) == zone); + JS::MarkStringAsLive(zone, cache->mString); + rval.setString(cache->mString); + *sharedBuffer = false; + return true; + } + + JSString* str = JS_NewExternalString(cx, + static_cast<char16_t*>(buf->Data()), + length, &sDOMStringFinalizer); + if (!str) { + return false; + } + rval.setString(str); + if (!cache) { + cache = new ZoneStringCache(); + JS_SetZoneUserData(zone, cache); + } + cache->mBuffer = buf; + cache->mLength = length; + cache->mString = str; + *sharedBuffer = true; + return true; + } + + static void FreeZoneCache(JS::Zone* zone); + static void ClearZoneCache(JS::Zone* zone); + + static MOZ_ALWAYS_INLINE bool IsLiteral(JSString* str) + { + return JS_IsExternalString(str) && + JS_GetExternalStringFinalizer(str) == &sLiteralFinalizer; + } + + static MOZ_ALWAYS_INLINE bool IsDOMString(JSString* str) + { + return JS_IsExternalString(str) && + JS_GetExternalStringFinalizer(str) == &sDOMStringFinalizer; + } + +private: + static const JSStringFinalizer sLiteralFinalizer, sDOMStringFinalizer; + + static void FinalizeLiteral(JS::Zone* zone, const JSStringFinalizer* fin, char16_t* chars); + + static void FinalizeDOMString(JS::Zone* zone, const JSStringFinalizer* fin, char16_t* chars); + + XPCStringConvert() = delete; +}; + +class nsIAddonInterposition; + +namespace xpc { + +// If these functions return false, then an exception will be set on cx. +bool Base64Encode(JSContext* cx, JS::HandleValue val, JS::MutableHandleValue out); +bool Base64Decode(JSContext* cx, JS::HandleValue val, JS::MutableHandleValue out); + +/** + * Convert an nsString to jsval, returning true on success. + * Note, the ownership of the string buffer may be moved from str to rval. + * If that happens, str will point to an empty string after this call. + */ +bool NonVoidStringToJsval(JSContext* cx, nsAString& str, JS::MutableHandleValue rval); +inline bool StringToJsval(JSContext* cx, nsAString& str, JS::MutableHandleValue rval) +{ + // From the T_DOMSTRING case in XPCConvert::NativeData2JS. + if (str.IsVoid()) { + rval.setNull(); + return true; + } + return NonVoidStringToJsval(cx, str, rval); +} + +inline bool +NonVoidStringToJsval(JSContext* cx, const nsAString& str, JS::MutableHandleValue rval) +{ + nsString mutableCopy; + if (!mutableCopy.Assign(str, mozilla::fallible)) { + JS_ReportOutOfMemory(cx); + return false; + } + return NonVoidStringToJsval(cx, mutableCopy, rval); +} + +inline bool +StringToJsval(JSContext* cx, const nsAString& str, JS::MutableHandleValue rval) +{ + nsString mutableCopy; + if (!mutableCopy.Assign(str, mozilla::fallible)) { + JS_ReportOutOfMemory(cx); + return false; + } + return StringToJsval(cx, mutableCopy, rval); +} + +/** + * As above, but for mozilla::dom::DOMString. + */ +inline +bool NonVoidStringToJsval(JSContext* cx, mozilla::dom::DOMString& str, + JS::MutableHandleValue rval) +{ + if (!str.HasStringBuffer()) { + // It's an actual XPCOM string + return NonVoidStringToJsval(cx, str.AsAString(), rval); + } + + uint32_t length = str.StringBufferLength(); + if (length == 0) { + rval.set(JS_GetEmptyStringValue(cx)); + return true; + } + + nsStringBuffer* buf = str.StringBuffer(); + bool shared; + if (!XPCStringConvert::StringBufferToJSVal(cx, buf, length, rval, + &shared)) { + return false; + } + if (shared) { + // JS now needs to hold a reference to the buffer + str.RelinquishBufferOwnership(); + } + return true; +} + +MOZ_ALWAYS_INLINE +bool StringToJsval(JSContext* cx, mozilla::dom::DOMString& str, + JS::MutableHandleValue rval) +{ + if (str.IsNull()) { + rval.setNull(); + return true; + } + return NonVoidStringToJsval(cx, str, rval); +} + +nsIPrincipal* GetCompartmentPrincipal(JSCompartment* compartment); + +void SetLocationForGlobal(JSObject* global, const nsACString& location); +void SetLocationForGlobal(JSObject* global, nsIURI* locationURI); + +// ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member +// of JS::ZoneStats. +class ZoneStatsExtras { +public: + ZoneStatsExtras() {} + + nsCString pathPrefix; + +private: + ZoneStatsExtras(const ZoneStatsExtras& other) = delete; + ZoneStatsExtras& operator=(const ZoneStatsExtras& other) = delete; +}; + +// ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member +// of JS::CompartmentStats. +class CompartmentStatsExtras { +public: + CompartmentStatsExtras() {} + + nsCString jsPathPrefix; + nsCString domPathPrefix; + nsCOMPtr<nsIURI> location; + +private: + CompartmentStatsExtras(const CompartmentStatsExtras& other) = delete; + CompartmentStatsExtras& operator=(const CompartmentStatsExtras& other) = delete; +}; + +// This reports all the stats in |rtStats| that belong in the "explicit" tree, +// (which isn't all of them). +// @see ZoneStatsExtras +// @see CompartmentStatsExtras +void +ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats, + const nsACString& rtPath, + nsIMemoryReporterCallback* handleReport, + nsISupports* data, + bool anonymize, + size_t* rtTotal = nullptr); + +/** + * Throws an exception on cx and returns false. + */ +bool +Throw(JSContext* cx, nsresult rv); + +/** + * Returns the nsISupports native behind a given reflector (either DOM or + * XPCWN). + */ +already_AddRefed<nsISupports> +UnwrapReflectorToISupports(JSObject* reflector); + +/** + * Singleton scopes for stuff that really doesn't fit anywhere else. + * + * If you find yourself wanting to use these compartments, you're probably doing + * something wrong. Callers MUST consult with the XPConnect module owner before + * using this compartment. If you don't, bholley will hunt you down. + */ +JSObject* +UnprivilegedJunkScope(); + +JSObject* +PrivilegedJunkScope(); + +/** + * Shared compilation scope for XUL prototype documents and XBL + * precompilation. This compartment has a null principal. No code may run, and + * it is invisible to the debugger. + */ +JSObject* +CompilationScope(); + +/** + * Returns the nsIGlobalObject corresponding to |aObj|'s JS global. + */ +nsIGlobalObject* +NativeGlobal(JSObject* aObj); + +/** + * If |aObj| is a window, returns the associated nsGlobalWindow. + * Otherwise, returns null. + */ +nsGlobalWindow* +WindowOrNull(JSObject* aObj); + +/** + * If |aObj| has a window for a global, returns the associated nsGlobalWindow. + * Otherwise, returns null. + */ +nsGlobalWindow* +WindowGlobalOrNull(JSObject* aObj); + +/** + * If |aObj| is in an addon scope and that addon scope is associated with a + * live DOM Window, returns the associated nsGlobalWindow. Otherwise, returns + * null. + */ +nsGlobalWindow* +AddonWindowOrNull(JSObject* aObj); + +/** + * If |cx| is in a compartment whose global is a window, returns the associated + * nsGlobalWindow. Otherwise, returns null. + */ +nsGlobalWindow* +CurrentWindowOrNull(JSContext* cx); + +void +SimulateActivityCallback(bool aActive); + +// This function may be used off-main-thread, in which case it is benignly +// racey. +bool +ShouldDiscardSystemSource(); + +bool +SharedMemoryEnabled(); + +bool +SetAddonInterposition(const nsACString& addonId, nsIAddonInterposition* interposition); + +bool +AllowCPOWsInAddon(const nsACString& addonId, bool allow); + +bool +ExtraWarningsForSystemJS(); + +class ErrorReport { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ErrorReport); + + ErrorReport() : mWindowID(0) + , mLineNumber(0) + , mColumn(0) + , mFlags(0) + , mIsMuted(false) + {} + + void Init(JSErrorReport* aReport, const char* aToStringResult, + bool aIsChrome, uint64_t aWindowID); + void Init(JSContext* aCx, mozilla::dom::Exception* aException, + bool aIsChrome, uint64_t aWindowID); + // Log the error report to the console. Which console will depend on the + // window id it was initialized with. + void LogToConsole(); + // Log to console, using the given stack object (which should be a stack of + // the sort that JS::CaptureCurrentStack produces). aStack is allowed to be + // null. + void LogToConsoleWithStack(JS::HandleObject aStack); + + // Produce an error event message string from the given JSErrorReport. Note + // that this may produce an empty string if aReport doesn't have a + // message attached. + static void ErrorReportToMessageString(JSErrorReport* aReport, + nsAString& aString); + + public: + + nsCString mCategory; + nsString mErrorMsgName; + nsString mErrorMsg; + nsString mFileName; + nsString mSourceLine; + uint64_t mWindowID; + uint32_t mLineNumber; + uint32_t mColumn; + uint32_t mFlags; + bool mIsMuted; + + private: + ~ErrorReport() {} +}; + +void +DispatchScriptErrorEvent(nsPIDOMWindowInner* win, JS::RootingContext* rootingCx, + xpc::ErrorReport* xpcReport, JS::Handle<JS::Value> exception); + +// Get a stack of the sort that can be passed to +// xpc::ErrorReport::LogToConsoleWithStack from the given exception value. Can +// return null if the exception value doesn't have an associated stack. The +// returned stack, if any, may also not be in the same compartment as +// exceptionValue. +// +// The "win" argument passed in here should be the same as the window whose +// WindowID() is used to initialize the xpc::ErrorReport. This may be null, of +// course. If it's not null, this function may return a null stack object if +// the window is far enough gone, because in those cases we don't want to have +// the stack in the console message keeping the window alive. +JSObject* +FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win, + JS::HandleValue exceptionValue); + +// Return a name for the compartment. +// This function makes reasonable efforts to make this name both mostly human-readable +// and unique. However, there are no guarantees of either property. +extern void +GetCurrentCompartmentName(JSContext*, nsCString& name); + +void AddGCCallback(xpcGCCallback cb); +void RemoveGCCallback(xpcGCCallback cb); + +inline bool +AreNonLocalConnectionsDisabled() +{ + static int disabledForTest = -1; + if (disabledForTest == -1) { + char *s = getenv("MOZ_DISABLE_NONLOCAL_CONNECTIONS"); + if (s) { + disabledForTest = *s != '0'; + } else { + disabledForTest = 0; + } + } + return disabledForTest; +} + +inline bool +IsInAutomation() +{ + const char* prefName = + "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer"; + return mozilla::Preferences::GetBool(prefName) && + AreNonLocalConnectionsDisabled(); +} + +} // namespace xpc + +namespace mozilla { +namespace dom { + +/** + * A test for whether WebIDL methods that should only be visible to + * chrome or XBL scopes should be exposed. + */ +bool IsChromeOrXBL(JSContext* cx, JSObject* /* unused */); + +/** + * Same as IsChromeOrXBL but can be used in worker threads as well. + */ +bool ThreadSafeIsChromeOrXBL(JSContext* cx, JSObject* obj); + +} // namespace dom +} // namespace mozilla + +#endif |