michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef xpcpublic_h michael@0: #define xpcpublic_h michael@0: michael@0: #include "jsapi.h" michael@0: #include "jsproxy.h" michael@0: #include "js/HeapAPI.h" michael@0: #include "js/GCAPI.h" michael@0: michael@0: #include "nsISupports.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsWrapperCache.h" michael@0: #include "nsStringGlue.h" michael@0: #include "nsTArray.h" michael@0: #include "mozilla/dom/JSSlots.h" michael@0: #include "nsMathUtils.h" michael@0: #include "nsStringBuffer.h" michael@0: #include "mozilla/dom/BindingDeclarations.h" michael@0: michael@0: class nsGlobalWindow; michael@0: class nsIPrincipal; michael@0: class nsScriptNameSpaceManager; michael@0: class nsIGlobalObject; michael@0: class nsIMemoryReporterCallback; michael@0: michael@0: #ifndef BAD_TLS_INDEX michael@0: #define BAD_TLS_INDEX ((uint32_t) -1) michael@0: #endif michael@0: michael@0: namespace xpc { michael@0: michael@0: class Scriptability { michael@0: public: michael@0: Scriptability(JSCompartment *c); michael@0: bool Allowed(); michael@0: bool IsImmuneToScriptPolicy(); michael@0: michael@0: void Block(); michael@0: void Unblock(); michael@0: void SetDocShellAllowsScript(bool aAllowed); michael@0: michael@0: static Scriptability& Get(JSObject *aScope); michael@0: michael@0: private: michael@0: // Whenever a consumer wishes to prevent script from running on a global, michael@0: // it increments this value with a call to Block(). When it wishes to michael@0: // re-enable it (if ever), it decrements this value with a call to Unblock(). michael@0: // Script may not run if this value is non-zero. michael@0: uint32_t mScriptBlocks; michael@0: michael@0: // Whether the docshell allows javascript in this scope. If this scope michael@0: // doesn't have a docshell, this value is always true. michael@0: bool mDocShellAllowsScript; michael@0: michael@0: // Whether this scope is immune to user-defined or addon-defined script michael@0: // policy. michael@0: bool mImmuneToScriptPolicy; michael@0: michael@0: // Whether the new-style domain policy when this compartment was created michael@0: // forbids script execution. michael@0: bool mScriptBlockedByPolicy; michael@0: }; michael@0: michael@0: JSObject * michael@0: TransplantObject(JSContext *cx, JS::HandleObject origobj, JS::HandleObject target); michael@0: michael@0: bool IsXBLScope(JSCompartment *compartment); michael@0: bool IsInXBLScope(JSObject *obj); michael@0: michael@0: // Return a raw XBL scope object corresponding to contentScope, which must michael@0: // be an object whose global is a DOM window. michael@0: // michael@0: // The return value is not wrapped into cx->compartment, so be sure to enter michael@0: // its compartment before doing anything meaningful. michael@0: // michael@0: // Also note that XBL scopes are lazily created, so the return-value should be michael@0: // null-checked unless the caller can ensure that the scope must already michael@0: // exist. michael@0: // michael@0: // This function asserts if |contentScope| is itself in an XBL scope to catch michael@0: // sloppy consumers. Conversely, GetXBLScopeOrGlobal will handle objects that michael@0: // are in XBL scope (by just returning the global). michael@0: JSObject * michael@0: GetXBLScope(JSContext *cx, JSObject *contentScope); michael@0: michael@0: inline JSObject * michael@0: GetXBLScopeOrGlobal(JSContext *cx, JSObject *obj) { michael@0: if (IsInXBLScope(obj)) michael@0: return js::GetGlobalForObjectCrossCompartment(obj); michael@0: return GetXBLScope(cx, obj); michael@0: } michael@0: michael@0: // Returns whether XBL scopes have been explicitly disabled for code running michael@0: // in this compartment. See the comment around mAllowXBLScope. michael@0: bool michael@0: AllowXBLScope(JSCompartment *c); michael@0: michael@0: // Returns whether we will use an XBL scope for this compartment. This is michael@0: // semantically equivalent to comparing global != GetXBLScope(global), but it michael@0: // does not have the side-effect of eagerly creating the XBL scope if it does michael@0: // not already exist. michael@0: bool michael@0: UseXBLScope(JSCompartment *c); michael@0: michael@0: bool michael@0: IsSandboxPrototypeProxy(JSObject *obj); michael@0: michael@0: bool michael@0: IsReflector(JSObject *obj); michael@0: michael@0: bool michael@0: IsXrayWrapper(JSObject *obj); michael@0: michael@0: // If this function was created for a given XrayWrapper, returns the global of michael@0: // the Xrayed object. Otherwise, returns the global of the function. michael@0: // michael@0: // To emphasize the obvious: the return value here is not necessarily same- michael@0: // compartment with the argument. michael@0: JSObject * michael@0: XrayAwareCalleeGlobal(JSObject *fun); michael@0: michael@0: void michael@0: TraceXPCGlobal(JSTracer *trc, JSObject *obj); michael@0: michael@0: } /* namespace xpc */ michael@0: michael@0: namespace JS { michael@0: michael@0: struct RuntimeStats; michael@0: michael@0: } michael@0: michael@0: #define XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(n) \ michael@0: JSCLASS_DOM_GLOBAL | JSCLASS_HAS_PRIVATE | \ michael@0: JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_IMPLEMENTS_BARRIERS | \ michael@0: JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS + n) michael@0: michael@0: #define XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET (JSCLASS_GLOBAL_SLOT_COUNT + DOM_GLOBAL_SLOTS) michael@0: michael@0: #define XPCONNECT_GLOBAL_FLAGS XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(0) michael@0: michael@0: inline JSObject* michael@0: xpc_FastGetCachedWrapper(JSContext *cx, nsWrapperCache *cache, JS::MutableHandleValue vp) michael@0: { michael@0: if (cache) { michael@0: JSObject* wrapper = cache->GetWrapper(); michael@0: if (wrapper && michael@0: js::GetObjectCompartment(wrapper) == js::GetContextCompartment(cx)) michael@0: { michael@0: vp.setObject(*wrapper); michael@0: return wrapper; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: // The JS GC marks objects gray that are held alive directly or michael@0: // indirectly by an XPConnect root. The cycle collector explores only michael@0: // this subset of the JS heap. michael@0: inline bool michael@0: xpc_IsGrayGCThing(void *thing) michael@0: { michael@0: return JS::GCThingIsMarkedGray(thing); michael@0: } michael@0: michael@0: // The cycle collector only cares about some kinds of GCthings that are michael@0: // reachable from an XPConnect root. Implemented in nsXPConnect.cpp. michael@0: extern bool michael@0: xpc_GCThingIsGrayCCThing(void *thing); michael@0: michael@0: inline JSScript * michael@0: xpc_UnmarkGrayScript(JSScript *script) michael@0: { michael@0: if (script) michael@0: JS::ExposeGCThingToActiveJS(script, JSTRACE_SCRIPT); michael@0: michael@0: return script; michael@0: } michael@0: michael@0: // If aVariant is an XPCVariant, this marks the object to be in aGeneration. michael@0: // This also unmarks the gray JSObject. michael@0: extern void michael@0: xpc_MarkInCCGeneration(nsISupports* aVariant, uint32_t aGeneration); michael@0: michael@0: // If aWrappedJS is a JS wrapper, unmark its JSObject. michael@0: extern void michael@0: xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS); michael@0: michael@0: extern void michael@0: xpc_UnmarkSkippableJSHolders(); michael@0: michael@0: // No JS can be on the stack when this is called. Probably only useful from michael@0: // xpcshell. michael@0: void michael@0: xpc_ActivateDebugMode(); michael@0: michael@0: // readable string conversions, static methods and members only michael@0: class XPCStringConvert michael@0: { michael@0: // One-slot cache, because it turns out it's common for web pages to michael@0: // get the same string a few times in a row. We get about a 40% cache michael@0: // hit rate on this cache last it was measured. We'd get about 70% michael@0: // hit rate with a hashtable with removal on finalization, but that michael@0: // would take a lot more machinery. michael@0: struct ZoneStringCache michael@0: { michael@0: nsStringBuffer* mBuffer; michael@0: JSString* mString; michael@0: }; michael@0: michael@0: public: michael@0: michael@0: // If the string shares the readable's buffer, that buffer will michael@0: // get assigned to *sharedBuffer. Otherwise null will be michael@0: // assigned. michael@0: static bool ReadableToJSVal(JSContext *cx, const nsAString &readable, michael@0: nsStringBuffer** sharedBuffer, michael@0: JS::MutableHandleValue vp); michael@0: michael@0: // Convert the given stringbuffer/length pair to a jsval michael@0: static MOZ_ALWAYS_INLINE bool michael@0: StringBufferToJSVal(JSContext* cx, nsStringBuffer* buf, uint32_t length, michael@0: JS::MutableHandleValue rval, bool* sharedBuffer) michael@0: { michael@0: JS::Zone *zone = js::GetContextZone(cx); michael@0: ZoneStringCache *cache = static_cast(JS_GetZoneUserData(zone)); michael@0: if (cache && buf == cache->mBuffer) { michael@0: MOZ_ASSERT(JS::GetGCThingZone(cache->mString) == zone); michael@0: JS::MarkStringAsLive(zone, cache->mString); michael@0: rval.setString(cache->mString); michael@0: *sharedBuffer = false; michael@0: return true; michael@0: } michael@0: michael@0: JSString *str = JS_NewExternalString(cx, michael@0: static_cast(buf->Data()), michael@0: length, &sDOMStringFinalizer); michael@0: if (!str) { michael@0: return false; michael@0: } michael@0: rval.setString(str); michael@0: if (!cache) { michael@0: cache = new ZoneStringCache(); michael@0: JS_SetZoneUserData(zone, cache); michael@0: } michael@0: cache->mBuffer = buf; michael@0: cache->mString = str; michael@0: *sharedBuffer = true; michael@0: return true; michael@0: } michael@0: michael@0: static void FreeZoneCache(JS::Zone *zone); michael@0: static void ClearZoneCache(JS::Zone *zone); michael@0: michael@0: static MOZ_ALWAYS_INLINE bool IsLiteral(JSString *str) michael@0: { michael@0: return JS_IsExternalString(str) && michael@0: JS_GetExternalStringFinalizer(str) == &sLiteralFinalizer; michael@0: } michael@0: michael@0: static MOZ_ALWAYS_INLINE bool IsDOMString(JSString *str) michael@0: { michael@0: return JS_IsExternalString(str) && michael@0: JS_GetExternalStringFinalizer(str) == &sDOMStringFinalizer; michael@0: } michael@0: michael@0: private: michael@0: static const JSStringFinalizer sLiteralFinalizer, sDOMStringFinalizer; michael@0: michael@0: static void FinalizeLiteral(const JSStringFinalizer *fin, jschar *chars); michael@0: michael@0: static void FinalizeDOMString(const JSStringFinalizer *fin, jschar *chars); michael@0: michael@0: XPCStringConvert(); // not implemented michael@0: }; michael@0: michael@0: namespace xpc { michael@0: michael@0: // If these functions return false, then an exception will be set on cx. michael@0: bool Base64Encode(JSContext *cx, JS::HandleValue val, JS::MutableHandleValue out); michael@0: bool Base64Decode(JSContext *cx, JS::HandleValue val, JS::MutableHandleValue out); michael@0: michael@0: /** michael@0: * Convert an nsString to jsval, returning true on success. michael@0: * Note, the ownership of the string buffer may be moved from str to rval. michael@0: * If that happens, str will point to an empty string after this call. michael@0: */ michael@0: bool NonVoidStringToJsval(JSContext *cx, nsAString &str, JS::MutableHandleValue rval); michael@0: inline bool StringToJsval(JSContext *cx, nsAString &str, JS::MutableHandleValue rval) michael@0: { michael@0: // From the T_DOMSTRING case in XPCConvert::NativeData2JS. michael@0: if (str.IsVoid()) { michael@0: rval.setNull(); michael@0: return true; michael@0: } michael@0: return NonVoidStringToJsval(cx, str, rval); michael@0: } michael@0: michael@0: inline bool michael@0: NonVoidStringToJsval(JSContext* cx, const nsAString& str, JS::MutableHandleValue rval) michael@0: { michael@0: nsString mutableCopy(str); michael@0: return NonVoidStringToJsval(cx, mutableCopy, rval); michael@0: } michael@0: michael@0: inline bool michael@0: StringToJsval(JSContext* cx, const nsAString& str, JS::MutableHandleValue rval) michael@0: { michael@0: nsString mutableCopy(str); michael@0: return StringToJsval(cx, mutableCopy, rval); michael@0: } michael@0: michael@0: /** michael@0: * As above, but for mozilla::dom::DOMString. michael@0: */ michael@0: MOZ_ALWAYS_INLINE michael@0: bool NonVoidStringToJsval(JSContext* cx, mozilla::dom::DOMString& str, michael@0: JS::MutableHandleValue rval) michael@0: { michael@0: if (!str.HasStringBuffer()) { michael@0: // It's an actual XPCOM string michael@0: return NonVoidStringToJsval(cx, str.AsAString(), rval); michael@0: } michael@0: michael@0: uint32_t length = str.StringBufferLength(); michael@0: if (length == 0) { michael@0: rval.set(JS_GetEmptyStringValue(cx)); michael@0: return true; michael@0: } michael@0: michael@0: nsStringBuffer* buf = str.StringBuffer(); michael@0: bool shared; michael@0: if (!XPCStringConvert::StringBufferToJSVal(cx, buf, length, rval, michael@0: &shared)) { michael@0: return false; michael@0: } michael@0: if (shared) { michael@0: // JS now needs to hold a reference to the buffer michael@0: buf->AddRef(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE michael@0: bool StringToJsval(JSContext* cx, mozilla::dom::DOMString& str, michael@0: JS::MutableHandleValue rval) michael@0: { michael@0: if (str.IsNull()) { michael@0: rval.setNull(); michael@0: return true; michael@0: } michael@0: return NonVoidStringToJsval(cx, str, rval); michael@0: } michael@0: michael@0: nsIPrincipal *GetCompartmentPrincipal(JSCompartment *compartment); michael@0: michael@0: void SetLocationForGlobal(JSObject *global, const nsACString& location); michael@0: void SetLocationForGlobal(JSObject *global, nsIURI *locationURI); michael@0: michael@0: // ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member michael@0: // of JS::ZoneStats. michael@0: class ZoneStatsExtras { michael@0: public: michael@0: ZoneStatsExtras() michael@0: {} michael@0: michael@0: nsAutoCString pathPrefix; michael@0: michael@0: private: michael@0: ZoneStatsExtras(const ZoneStatsExtras &other) MOZ_DELETE; michael@0: ZoneStatsExtras& operator=(const ZoneStatsExtras &other) MOZ_DELETE; michael@0: }; michael@0: michael@0: // ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member michael@0: // of JS::CompartmentStats. michael@0: class CompartmentStatsExtras { michael@0: public: michael@0: CompartmentStatsExtras() michael@0: {} michael@0: michael@0: nsAutoCString jsPathPrefix; michael@0: nsAutoCString domPathPrefix; michael@0: nsCOMPtr location; michael@0: michael@0: private: michael@0: CompartmentStatsExtras(const CompartmentStatsExtras &other) MOZ_DELETE; michael@0: CompartmentStatsExtras& operator=(const CompartmentStatsExtras &other) MOZ_DELETE; michael@0: }; michael@0: michael@0: // This reports all the stats in |rtStats| that belong in the "explicit" tree, michael@0: // (which isn't all of them). michael@0: // @see ZoneStatsExtras michael@0: // @see CompartmentStatsExtras michael@0: nsresult michael@0: ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, michael@0: const nsACString &rtPath, michael@0: nsIMemoryReporterCallback *cb, michael@0: nsISupports *closure, size_t *rtTotal = nullptr); michael@0: michael@0: /** michael@0: * Throws an exception on cx and returns false. michael@0: */ michael@0: bool michael@0: Throw(JSContext *cx, nsresult rv); michael@0: michael@0: /** michael@0: * Every global should hold a native that implements the nsIGlobalObject interface. michael@0: */ michael@0: nsIGlobalObject * michael@0: GetNativeForGlobal(JSObject *global); michael@0: michael@0: /** michael@0: * In some cases a native object does not really belong to any compartment (XBL, michael@0: * document created from by XHR of a worker, etc.). But when for some reason we michael@0: * have to wrap these natives (because of an event for example) instead of just michael@0: * wrapping them into some random compartment we find on the context stack (like michael@0: * we did previously) a default compartment is used. This function returns that michael@0: * compartment's global. It is a singleton on the runtime. michael@0: * If you find yourself wanting to use this compartment, you're probably doing michael@0: * something wrong. Callers MUST consult with the XPConnect module owner before michael@0: * using this compartment. If you don't, bholley will hunt you down. michael@0: */ michael@0: JSObject * michael@0: GetJunkScope(); michael@0: michael@0: /** michael@0: * Returns the native global of the junk scope. See comment of GetJunkScope michael@0: * about the conditions of using it. michael@0: */ michael@0: nsIGlobalObject * michael@0: GetJunkScopeGlobal(); michael@0: michael@0: /** michael@0: * Shared compilation scope for XUL prototype documents and XBL michael@0: * precompilation. This compartment has a null principal. No code may run, and michael@0: * it is invisible to the debugger. michael@0: */ michael@0: JSObject * michael@0: GetCompilationScope(); michael@0: michael@0: /** michael@0: * If |aObj| is a window, returns the associated nsGlobalWindow. michael@0: * Otherwise, returns null. michael@0: */ michael@0: nsGlobalWindow* michael@0: WindowOrNull(JSObject *aObj); michael@0: michael@0: /* michael@0: * Returns the dummy global associated with the SafeJSContext. Callers MUST michael@0: * consult with the XPConnect module owner before using this function. michael@0: */ michael@0: JSObject * michael@0: GetSafeJSContextGlobal(); michael@0: michael@0: /** michael@0: * If |aObj| has a window for a global, returns the associated nsGlobalWindow. michael@0: * Otherwise, returns null. michael@0: */ michael@0: nsGlobalWindow* michael@0: WindowGlobalOrNull(JSObject *aObj); michael@0: michael@0: // Error reporter used when there is no associated DOM window on to which to michael@0: // report errors and warnings. michael@0: void michael@0: SystemErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep); michael@0: michael@0: void michael@0: SimulateActivityCallback(bool aActive); michael@0: michael@0: void michael@0: RecordAdoptedNode(JSCompartment *c); michael@0: michael@0: void michael@0: RecordDonatedNode(JSCompartment *c); michael@0: michael@0: // This function may be used off-main-thread, in which case it is benignly michael@0: // racey. michael@0: bool michael@0: ShouldDiscardSystemSource(); michael@0: michael@0: } // namespace xpc michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: typedef JSObject* michael@0: (*DefineInterface)(JSContext *cx, JS::Handle global, michael@0: JS::Handle id, bool defineOnGlobal); michael@0: michael@0: typedef JSObject* michael@0: (*ConstructNavigatorProperty)(JSContext *cx, JS::Handle naviObj); michael@0: michael@0: // Check whether a constructor should be enabled for the given object. michael@0: // Note that the object should NOT be an Xray, since Xrays will end up michael@0: // defining constructors on the underlying object. michael@0: // This is a typedef for the function type itself, not the function michael@0: // pointer, so it's more obvious that pointers to a ConstructorEnabled michael@0: // can be null. michael@0: typedef bool michael@0: (ConstructorEnabled)(JSContext* cx, JS::Handle obj); michael@0: michael@0: void michael@0: Register(nsScriptNameSpaceManager* aNameSpaceManager); michael@0: michael@0: /** michael@0: * A test for whether WebIDL methods that should only be visible to michael@0: * chrome or XBL scopes should be exposed. michael@0: */ michael@0: bool IsChromeOrXBL(JSContext* cx, JSObject* /* unused */); michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: #endif