bool NS_IsMainThread() { return sTLSIsMainThread.get(); }
GrGLvoid glDeleteProgram_mozilla(GrGLuint program) { return sGLContext.get()->fDeleteProgram(program); }
GrGLvoid glDeleteShader_mozilla(GrGLuint shader) { return sGLContext.get()->fDeleteShader(shader); }
GrGLuint glCreateProgram_mozilla(void) { return sGLContext.get()->fCreateProgram(); }
GrGLvoid glCullFace_mozilla(GrGLenum mode) { return sGLContext.get()->fCullFace(mode); }
GrGLvoid glClearStencil_mozilla(GrGLint s) { return sGLContext.get()->fClearStencil(s); }
GrGLvoid glCompileShader_mozilla(GrGLuint shader) { return sGLContext.get()->fCompileShader(shader); }
void FreeTraceInfo() { FreeTraceInfo(sTraceInfoTLS.get()); }
namespace tasktracer { static mozilla::ThreadLocal<TraceInfo*> sTraceInfoTLS; static mozilla::StaticMutex sMutex; // The generation of TraceInfo. It will be > 0 if the Task Tracer is started and // <= 0 if stopped. static mozilla::Atomic<bool> sStarted; static nsTArray<nsAutoPtr<TraceInfo>>* sTraceInfos = nullptr; static PRTime sStartTime; static const char sJSLabelPrefix[] = "#tt#"; namespace { static PRTime GetTimestamp() { return PR_Now() / 1000; } static TraceInfo* AllocTraceInfo(int aTid) { StaticMutexAutoLock lock(sMutex); nsAutoPtr<TraceInfo>* info = sTraceInfos->AppendElement( new TraceInfo(aTid)); return info->get(); } static void SaveCurTraceInfo() { TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); info->mSavedCurTraceSourceId = info->mCurTraceSourceId; info->mSavedCurTraceSourceType = info->mCurTraceSourceType; info->mSavedCurTaskId = info->mCurTaskId; } static void RestoreCurTraceInfo() { TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); info->mCurTraceSourceId = info->mSavedCurTraceSourceId; info->mCurTraceSourceType = info->mSavedCurTraceSourceType; info->mCurTaskId = info->mSavedCurTaskId; } static void CreateSourceEvent(SourceEventType aType) { // Save the currently traced source event info. SaveCurTraceInfo(); // Create a new unique task id. uint64_t newId = GenNewUniqueTaskId(); TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); info->mCurTraceSourceId = newId; info->mCurTraceSourceType = aType; info->mCurTaskId = newId; int* namePtr; #define SOURCE_EVENT_NAME(type) \ case SourceEventType::type: \ { \ static int CreateSourceEvent##type; \ namePtr = &CreateSourceEvent##type; \ break; \ } switch (aType) { #include "SourceEventTypeMap.h" default: MOZ_CRASH("Unknown SourceEvent."); }; #undef CREATE_SOURCE_EVENT_NAME // Log a fake dispatch and start for this source event. LogDispatch(newId, newId, newId, aType); LogVirtualTablePtr(newId, newId, namePtr); LogBegin(newId, newId); } static void DestroySourceEvent() { // Log a fake end for this source event. TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); LogEnd(info->mCurTraceSourceId, info->mCurTraceSourceId); // Restore the previously saved source event info. RestoreCurTraceInfo(); } inline static bool IsStartLogging() { return sStarted; } static void SetLogStarted(bool aIsStartLogging) { MOZ_ASSERT(aIsStartLogging != IsStartLogging()); sStarted = aIsStartLogging; StaticMutexAutoLock lock(sMutex); if (!aIsStartLogging) { for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) { (*sTraceInfos)[i]->mObsolete = true; } } } static void CleanUp() { SetLogStarted(false); StaticMutexAutoLock lock(sMutex); if (sTraceInfos) { delete sTraceInfos; sTraceInfos = nullptr; } } inline static void ObsoleteCurrentTraceInfos() { // Note that we can't and don't need to acquire sMutex here because this // function is called before the other threads are recreated. for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) { (*sTraceInfos)[i]->mObsolete = true; } } } // namespace anonymous nsCString* TraceInfo::AppendLog() { MutexAutoLock lock(mLogsMutex); return mLogs.AppendElement(); } void TraceInfo::MoveLogsInto(TraceInfoLogsType& aResult) { MutexAutoLock lock(mLogsMutex); aResult.AppendElements(Move(mLogs)); } void InitTaskTracer(uint32_t aFlags) { if (aFlags & FORKED_AFTER_NUWA) { ObsoleteCurrentTraceInfos(); return; } MOZ_ASSERT(!sTraceInfos); sTraceInfos = new nsTArray<nsAutoPtr<TraceInfo>>(); if (!sTraceInfoTLS.initialized()) { Unused << sTraceInfoTLS.init(); } } void ShutdownTaskTracer() { CleanUp(); } static void FreeTraceInfo(TraceInfo* aTraceInfo) { StaticMutexAutoLock lock(sMutex); if (aTraceInfo) { sTraceInfos->RemoveElement(aTraceInfo); } } void FreeTraceInfo() { FreeTraceInfo(sTraceInfoTLS.get()); } TraceInfo* GetOrCreateTraceInfo() { ENSURE_TRUE(sTraceInfoTLS.initialized(), nullptr); ENSURE_TRUE(IsStartLogging(), nullptr); TraceInfo* info = sTraceInfoTLS.get(); if (info && info->mObsolete) { // TraceInfo is obsolete: remove it. FreeTraceInfo(info); info = nullptr; } if (!info) { info = AllocTraceInfo(gettid()); sTraceInfoTLS.set(info); } return info; } uint64_t GenNewUniqueTaskId() { TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE(info, 0); pid_t tid = gettid(); uint64_t taskid = ((uint64_t)tid << 32) | ++info->mLastUniqueTaskId; return taskid; } AutoSaveCurTraceInfo::AutoSaveCurTraceInfo() { SaveCurTraceInfo(); } AutoSaveCurTraceInfo::~AutoSaveCurTraceInfo() { RestoreCurTraceInfo(); } void SetCurTraceInfo(uint64_t aSourceEventId, uint64_t aParentTaskId, SourceEventType aSourceEventType) { TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); info->mCurTraceSourceId = aSourceEventId; info->mCurTaskId = aParentTaskId; info->mCurTraceSourceType = aSourceEventType; } void GetCurTraceInfo(uint64_t* aOutSourceEventId, uint64_t* aOutParentTaskId, SourceEventType* aOutSourceEventType) { TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); *aOutSourceEventId = info->mCurTraceSourceId; *aOutParentTaskId = info->mCurTaskId; *aOutSourceEventType = info->mCurTraceSourceType; } void LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId, SourceEventType aSourceEventType) { LogDispatch(aTaskId, aParentTaskId, aSourceEventId, aSourceEventType, 0); } void LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId, SourceEventType aSourceEventType, int aDelayTimeMs) { TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); // aDelayTimeMs is the expected delay time in milliseconds, thus the dispatch // time calculated of it might be slightly off in the real world. uint64_t time = (aDelayTimeMs <= 0) ? GetTimestamp() : GetTimestamp() + aDelayTimeMs; // Log format: // [0 taskId dispatchTime sourceEventId sourceEventType parentTaskId] nsCString* log = info->AppendLog(); if (log) { log->AppendPrintf("%d %lld %lld %lld %d %lld", ACTION_DISPATCH, aTaskId, time, aSourceEventId, aSourceEventType, aParentTaskId); } } void LogBegin(uint64_t aTaskId, uint64_t aSourceEventId) { TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); // Log format: // [1 taskId beginTime processId threadId] nsCString* log = info->AppendLog(); if (log) { log->AppendPrintf("%d %lld %lld %d %d", ACTION_BEGIN, aTaskId, GetTimestamp(), getpid(), gettid()); } } void LogEnd(uint64_t aTaskId, uint64_t aSourceEventId) { TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); // Log format: // [2 taskId endTime] nsCString* log = info->AppendLog(); if (log) { log->AppendPrintf("%d %lld %lld", ACTION_END, aTaskId, GetTimestamp()); } } void LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId, int* aVptr) { TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); // Log format: // [4 taskId address] nsCString* log = info->AppendLog(); if (log) { log->AppendPrintf("%d %lld %p", ACTION_GET_VTABLE, aTaskId, aVptr); } } AutoSourceEvent::AutoSourceEvent(SourceEventType aType) { CreateSourceEvent(aType); } AutoSourceEvent::~AutoSourceEvent() { DestroySourceEvent(); } void AddLabel(const char* aFormat, ...) { TraceInfo* info = GetOrCreateTraceInfo(); ENSURE_TRUE_VOID(info); va_list args; va_start(args, aFormat); nsAutoCString buffer; buffer.AppendPrintf(aFormat, args); va_end(args); // Log format: // [3 taskId "label"] nsCString* log = info->AppendLog(); if (log) { log->AppendPrintf("%d %lld %lld \"%s\"", ACTION_ADD_LABEL, info->mCurTaskId, GetTimestamp(), buffer.get()); } } // Functions used by GeckoProfiler. void StartLogging() { sStartTime = GetTimestamp(); SetLogStarted(true); } void StopLogging() { SetLogStarted(false); } TraceInfoLogsType* GetLoggedData(TimeStamp aTimeStamp) { TraceInfoLogsType* result = new TraceInfoLogsType(); // TODO: This is called from a signal handler. Use semaphore instead. StaticMutexAutoLock lock(sMutex); for (uint32_t i = 0; i < sTraceInfos->Length(); ++i) { (*sTraceInfos)[i]->MoveLogsInto(*result); } return result; } const PRTime GetStartTime() { return sStartTime; } const char* GetJSLabelPrefix() { return sJSLabelPrefix; } #undef ENSURE_TRUE_VOID #undef ENSURE_TRUE } // namespace tasktracer
static ScriptSettingsStackEntry* Top() { return sScriptSettingsTLS.get(); }
static void Pop(ScriptSettingsStackEntry *aEntry) { MOZ_ASSERT(aEntry == Top()); sScriptSettingsTLS.set(aEntry->mOlder); }
namespace dom { static mozilla::ThreadLocal<ScriptSettingsStackEntry*> sScriptSettingsTLS; class ScriptSettingsStack { public: static ScriptSettingsStackEntry* Top() { return sScriptSettingsTLS.get(); } static void Push(ScriptSettingsStackEntry *aEntry) { MOZ_ASSERT(!aEntry->mOlder); // Whenever JSAPI use is disabled, the next stack entry pushed must // always be a candidate entry point. MOZ_ASSERT_IF(!Top() || Top()->NoJSAPI(), aEntry->mIsCandidateEntryPoint); aEntry->mOlder = Top(); sScriptSettingsTLS.set(aEntry); } static void Pop(ScriptSettingsStackEntry *aEntry) { MOZ_ASSERT(aEntry == Top()); sScriptSettingsTLS.set(aEntry->mOlder); } static nsIGlobalObject* IncumbentGlobal() { ScriptSettingsStackEntry *entry = Top(); return entry ? entry->mGlobalObject : nullptr; } static ScriptSettingsStackEntry* EntryPoint() { ScriptSettingsStackEntry *entry = Top(); if (!entry) { return nullptr; } while (entry) { if (entry->mIsCandidateEntryPoint) return entry; entry = entry->mOlder; } MOZ_CRASH("Non-empty stack should always have an entry point"); } static nsIGlobalObject* EntryGlobal() { ScriptSettingsStackEntry *entry = EntryPoint(); return entry ? entry->mGlobalObject : nullptr; } }; static unsigned long gRunToCompletionListeners = 0; void UseEntryScriptProfiling() { MOZ_ASSERT(NS_IsMainThread()); ++gRunToCompletionListeners; } void UnuseEntryScriptProfiling() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(gRunToCompletionListeners > 0); --gRunToCompletionListeners; } void InitScriptSettings() { if (!sScriptSettingsTLS.initialized()) { bool success = sScriptSettingsTLS.init(); if (!success) { MOZ_CRASH(); } } sScriptSettingsTLS.set(nullptr); } void DestroyScriptSettings() { MOZ_ASSERT(sScriptSettingsTLS.get() == nullptr); } bool ScriptSettingsInitialized() { return sScriptSettingsTLS.initialized(); } ScriptSettingsStackEntry::ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, bool aCandidate) : mGlobalObject(aGlobal) , mIsCandidateEntryPoint(aCandidate) , mOlder(nullptr) { MOZ_ASSERT(mGlobalObject); MOZ_ASSERT(mGlobalObject->GetGlobalJSObject(), "Must have an actual JS global for the duration on the stack"); MOZ_ASSERT(JS_IsGlobalObject(mGlobalObject->GetGlobalJSObject()), "No outer windows allowed"); ScriptSettingsStack::Push(this); } // This constructor is only for use by AutoNoJSAPI. ScriptSettingsStackEntry::ScriptSettingsStackEntry() : mGlobalObject(nullptr) , mIsCandidateEntryPoint(true) , mOlder(nullptr) { ScriptSettingsStack::Push(this); } ScriptSettingsStackEntry::~ScriptSettingsStackEntry() { // We must have an actual JS global for the entire time this is on the stack. MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject()); ScriptSettingsStack::Pop(this); } // If the entry or incumbent global ends up being something that the subject // principal doesn't subsume, we don't want to use it. This never happens on // the web, but can happen with asymmetric privilege relationships (i.e. // nsExpandedPrincipal and System Principal). // // The most correct thing to use instead would be the topmost global on the // callstack whose principal is subsumed by the subject principal. But that's // hard to compute, so we just substitute the global of the current // compartment. In practice, this is fine. // // Note that in particular things like: // // |SpecialPowers.wrap(crossOriginWindow).eval(open())| // // trigger this case. Although both the entry global and the current global // have normal principals, the use of Gecko-specific System-Principaled JS // puts the code from two different origins on the callstack at once, which // doesn't happen normally on the web. static nsIGlobalObject* ClampToSubject(nsIGlobalObject* aGlobalOrNull) { if (!aGlobalOrNull || !NS_IsMainThread()) { return aGlobalOrNull; } nsIPrincipal* globalPrin = aGlobalOrNull->PrincipalOrNull(); NS_ENSURE_TRUE(globalPrin, GetCurrentGlobal()); if (!nsContentUtils::SubjectPrincipal()->SubsumesConsideringDomain(globalPrin)) { return GetCurrentGlobal(); } return aGlobalOrNull; } nsIGlobalObject* GetEntryGlobal() { return ClampToSubject(ScriptSettingsStack::EntryGlobal()); } nsIDocument* GetEntryDocument() { nsIGlobalObject* global = GetEntryGlobal(); nsCOMPtr<nsPIDOMWindow> entryWin = do_QueryInterface(global); // If our entry global isn't a window, see if it's an addon scope associated // with a window. If it is, the caller almost certainly wants that rather // than null. if (!entryWin && global) { entryWin = xpc::AddonWindowOrNull(global->GetGlobalJSObject()); } return entryWin ? entryWin->GetExtantDoc() : nullptr; } nsIGlobalObject* GetIncumbentGlobal() { // We need the current JSContext in order to check the JS for // scripted frames that may have appeared since anyone last // manipulated the stack. If it's null, that means that there // must be no entry global on the stack, and therefore no incumbent // global either. JSContext *cx = nsContentUtils::GetCurrentJSContextForThread(); if (!cx) { MOZ_ASSERT(ScriptSettingsStack::EntryGlobal() == nullptr); return nullptr; } // See what the JS engine has to say. If we've got a scripted caller // override in place, the JS engine will lie to us and pretend that // there's nothing on the JS stack, which will cause us to check the // incumbent script stack below. if (JSObject *global = JS::GetScriptedCallerGlobal(cx)) { return ClampToSubject(xpc::NativeGlobal(global)); } // Ok, nothing from the JS engine. Let's use whatever's on the // explicit stack. return ClampToSubject(ScriptSettingsStack::IncumbentGlobal()); } nsIGlobalObject* GetCurrentGlobal() { JSContext *cx = nsContentUtils::GetCurrentJSContextForThread(); if (!cx) { return nullptr; } JSObject *global = JS::CurrentGlobalOrNull(cx); if (!global) { return nullptr; } return xpc::NativeGlobal(global); } nsIPrincipal* GetWebIDLCallerPrincipal() { MOZ_ASSERT(NS_IsMainThread()); ScriptSettingsStackEntry *entry = ScriptSettingsStack::EntryPoint(); // If we have an entry point that is not NoJSAPI, we know it must be an // AutoEntryScript. if (!entry || entry->NoJSAPI()) { return nullptr; } AutoEntryScript* aes = static_cast<AutoEntryScript*>(entry); // We can't yet rely on the Script Settings Stack to properly determine the // entry script, because there are still lots of places in the tree where we // don't yet use an AutoEntryScript (bug 951991 tracks this work). In the // mean time though, we can make some observations to hack around the // problem: // // (1) All calls into JS-implemented WebIDL go through CallSetup, which goes // through AutoEntryScript. // (2) The top candidate entry point in the Script Settings Stack is the // entry point if and only if no other JSContexts have been pushed on // top of the push made by that entry's AutoEntryScript. // // Because of (1), all of the cases where we might return a non-null // WebIDL Caller are guaranteed to have put an entry on the Script Settings // Stack, so we can restrict our search to that. Moreover, (2) gives us a // criterion to determine whether an entry in the Script Setting Stack means // that we should return a non-null WebIDL Caller. // // Once we fix bug 951991, this can all be simplified. if (!aes->CxPusherIsStackTop()) { return nullptr; } return aes->mWebIDLCallerPrincipal; } static JSContext* FindJSContext(nsIGlobalObject* aGlobalObject) { MOZ_ASSERT(NS_IsMainThread()); JSContext *cx = nullptr; nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobalObject); if (sgo && sgo->GetScriptContext()) { cx = sgo->GetScriptContext()->GetNativeContext(); } if (!cx) { cx = nsContentUtils::GetSafeJSContext(); } return cx; } AutoJSAPI::AutoJSAPI() : mCx(nullptr) , mOwnErrorReporting(false) , mOldDontReportUncaught(false) { } AutoJSAPI::~AutoJSAPI() { if (mOwnErrorReporting) { MOZ_ASSERT(NS_IsMainThread(), "See corresponding assertion in TakeOwnershipOfErrorReporting()"); JS::ContextOptionsRef(cx()).setDontReportUncaught(mOldDontReportUncaught); if (HasException()) { // AutoJSAPI uses a JSAutoNullableCompartment, and may be in a null // compartment when the destructor is called. However, the JS engine // requires us to be in a compartment when we fetch the pending exception. // In this case, we enter the privileged junk scope and don't dispatch any // error events. JS::Rooted<JSObject*> errorGlobal(cx(), JS::CurrentGlobalOrNull(cx())); if (!errorGlobal) errorGlobal = xpc::PrivilegedJunkScope(); JSAutoCompartment ac(cx(), errorGlobal); nsCOMPtr<nsPIDOMWindow> win = xpc::WindowGlobalOrNull(errorGlobal); const char *category = nsContentUtils::IsCallerChrome() ? "chrome javascript" : "content javascript"; JS::Rooted<JS::Value> exn(cx()); js::ErrorReport jsReport(cx()); if (StealException(&exn) && jsReport.init(cx(), exn)) { nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); xpcReport->Init(jsReport.report(), jsReport.message(), category, win ? win->WindowID() : 0); if (win) { DispatchScriptErrorEvent(win, JS_GetRuntime(cx()), xpcReport, exn); } else { xpcReport->LogToConsole(); } } else { NS_WARNING("OOMed while acquiring uncaught exception from JSAPI"); } } } if (mOldErrorReporter.isSome()) { JS_SetErrorReporter(JS_GetRuntime(cx()), mOldErrorReporter.value()); } } void AutoJSAPI::InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread) { MOZ_ASSERT(aCx); mCx = aCx; if (aIsMainThread) { // This Rooted<> is necessary only as long as AutoCxPusher::AutoCxPusher // can GC, which is only possible because XPCJSContextStack::Push calls // nsIPrincipal.Equals. Once that is removed, the Rooted<> will no longer // be necessary. JS::Rooted<JSObject*> global(JS_GetRuntime(aCx), aGlobal); mCxPusher.emplace(mCx); mAutoNullableCompartment.emplace(mCx, global); } else { mAutoNullableCompartment.emplace(mCx, aGlobal); } if (aIsMainThread) { JSRuntime* rt = JS_GetRuntime(aCx); mOldErrorReporter.emplace(JS_GetErrorReporter(rt)); JS_SetErrorReporter(rt, xpc::SystemErrorReporter); } } AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, JSContext* aCx) : mOwnErrorReporting(false) , mOldDontReportUncaught(false) { MOZ_ASSERT(aGlobalObject); MOZ_ASSERT(aGlobalObject->GetGlobalJSObject(), "Must have a JS global"); MOZ_ASSERT(aCx); MOZ_ASSERT_IF(aIsMainThread, NS_IsMainThread()); InitInternal(aGlobalObject->GetGlobalJSObject(), aCx, aIsMainThread); } void AutoJSAPI::Init() { MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once"); InitInternal(/* aGlobal */ nullptr, nsContentUtils::GetDefaultJSContextForThread(), NS_IsMainThread()); } bool AutoJSAPI::Init(nsIGlobalObject* aGlobalObject, JSContext* aCx) { MOZ_ASSERT(!mCx, "An AutoJSAPI should only be initialised once"); MOZ_ASSERT(aCx); if (NS_WARN_IF(!aGlobalObject)) { return false; } JSObject* global = aGlobalObject->GetGlobalJSObject(); if (NS_WARN_IF(!global)) { return false; } InitInternal(global, aCx, NS_IsMainThread()); return true; } bool AutoJSAPI::Init(nsIGlobalObject* aGlobalObject) { return Init(aGlobalObject, nsContentUtils::GetDefaultJSContextForThread()); } bool AutoJSAPI::Init(JSObject* aObject) { return Init(xpc::NativeGlobal(aObject)); } bool AutoJSAPI::InitWithLegacyErrorReporting(nsIGlobalObject* aGlobalObject) { MOZ_ASSERT(NS_IsMainThread()); return Init(aGlobalObject, FindJSContext(aGlobalObject)); } bool AutoJSAPI::Init(nsPIDOMWindow* aWindow, JSContext* aCx) { return Init(static_cast<nsGlobalWindow*>(aWindow), aCx); } bool AutoJSAPI::Init(nsPIDOMWindow* aWindow) { return Init(static_cast<nsGlobalWindow*>(aWindow)); } bool AutoJSAPI::Init(nsGlobalWindow* aWindow, JSContext* aCx) { return Init(static_cast<nsIGlobalObject*>(aWindow), aCx); } bool AutoJSAPI::Init(nsGlobalWindow* aWindow) { return Init(static_cast<nsIGlobalObject*>(aWindow)); } bool AutoJSAPI::InitWithLegacyErrorReporting(nsPIDOMWindow* aWindow) { return InitWithLegacyErrorReporting(static_cast<nsGlobalWindow*>(aWindow)); } bool AutoJSAPI::InitWithLegacyErrorReporting(nsGlobalWindow* aWindow) { return InitWithLegacyErrorReporting(static_cast<nsIGlobalObject*>(aWindow)); } // Even with dontReportUncaught, the JS engine still sends warning reports // to the JSErrorReporter as soon as they are generated. These go directly to // the console, so we can handle them easily here. // // Eventually, SpiderMonkey will have a special-purpose callback for warnings only. void WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aRep) { MOZ_ASSERT(JSREPORT_IS_WARNING(aRep->flags)); nsRefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); const char* category = nsContentUtils::IsCallerChrome() ? "chrome javascript" : "content javascript"; nsPIDOMWindow* win = xpc::WindowGlobalOrNull(JS::CurrentGlobalOrNull(aCx)); xpcReport->Init(aRep, aMessage, category, win ? win->WindowID() : 0); xpcReport->LogToConsole(); } void AutoJSAPI::TakeOwnershipOfErrorReporting() { MOZ_ASSERT(NS_IsMainThread(), "Can't own error reporting off-main-thread yet"); MOZ_ASSERT(!mOwnErrorReporting); mOwnErrorReporting = true; JSRuntime *rt = JS_GetRuntime(cx()); mOldDontReportUncaught = JS::ContextOptionsRef(cx()).dontReportUncaught(); JS::ContextOptionsRef(cx()).setDontReportUncaught(true); JS_SetErrorReporter(rt, WarningOnlyErrorReporter); } bool AutoJSAPI::StealException(JS::MutableHandle<JS::Value> aVal) { MOZ_ASSERT(CxPusherIsStackTop()); MOZ_ASSERT(HasException()); MOZ_ASSERT(js::GetContextCompartment(cx())); if (!JS_GetPendingException(cx(), aVal)) { return false; } JS_ClearPendingException(cx()); return true; } AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject, bool aIsMainThread, JSContext* aCx) : AutoJSAPI(aGlobalObject, aIsMainThread, aCx ? aCx : FindJSContext(aGlobalObject)) , ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true) , mWebIDLCallerPrincipal(nullptr) , mDocShellForJSRunToCompletion(nullptr) , mIsMainThread(aIsMainThread) { MOZ_ASSERT(aGlobalObject); MOZ_ASSERT_IF(!aCx, aIsMainThread); // cx is mandatory off-main-thread. MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == FindJSContext(aGlobalObject)); if (aIsMainThread) { nsContentUtils::EnterMicroTask(); } if (aIsMainThread && gRunToCompletionListeners > 0) { nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobalObject); if (window) { mDocShellForJSRunToCompletion = window->GetDocShell(); } } if (mDocShellForJSRunToCompletion) { mDocShellForJSRunToCompletion->NotifyJSRunToCompletionStart(); } } AutoEntryScript::~AutoEntryScript() { if (mDocShellForJSRunToCompletion) { mDocShellForJSRunToCompletion->NotifyJSRunToCompletionStop(); } if (mIsMainThread) { nsContentUtils::LeaveMicroTask(); } // GC when we pop a script entry point. This is a useful heuristic that helps // us out on certain (flawed) benchmarks like sunspider, because it lets us // avoid GCing during the timing loop. JS_MaybeGC(cx()); } AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject) : ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ false) , mCallerOverride(nsContentUtils::GetCurrentJSContextForThread()) { } AutoNoJSAPI::AutoNoJSAPI(bool aIsMainThread) : ScriptSettingsStackEntry() { MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContextForThread(), !JS_IsExceptionPending(nsContentUtils::GetCurrentJSContextForThread())); if (aIsMainThread) { mCxPusher.emplace(static_cast<JSContext*>(nullptr), /* aAllowNull = */ true); } } danger::AutoCxPusher::AutoCxPusher(JSContext* cx, bool allowNull) { MOZ_ASSERT_IF(!allowNull, cx); // Hold a strong ref to the nsIScriptContext, if any. This ensures that we // only destroy the mContext of an nsJSContext when it is not on the cx stack // (and therefore not in use). See nsJSContext::DestroyJSContext(). if (cx) mScx = GetScriptContextFromJSContext(cx); XPCJSContextStack *stack = XPCJSRuntime::Get()->GetJSContextStack(); if (!stack->Push(cx)) { MOZ_CRASH(); } mStackDepthAfterPush = stack->Count(); #ifdef DEBUG mPushedContext = cx; mCompartmentDepthOnEntry = cx ? js::GetEnterCompartmentDepth(cx) : 0; #endif // Enter a request and a compartment for the duration that the cx is on the // stack if non-null. if (cx) { mAutoRequest.emplace(cx); } } danger::AutoCxPusher::~AutoCxPusher() { // Leave the request before popping. mAutoRequest.reset(); // When we push a context, we may save the frame chain and pretend like we // haven't entered any compartment. This gets restored on Pop(), but we can // run into trouble if a Push/Pop are interleaved with a // JSAutoEnterCompartment. Make sure the compartment depth right before we // pop is the same as it was right after we pushed. MOZ_ASSERT_IF(mPushedContext, mCompartmentDepthOnEntry == js::GetEnterCompartmentDepth(mPushedContext)); DebugOnly<JSContext*> stackTop; MOZ_ASSERT(mPushedContext == nsXPConnect::XPConnect()->GetCurrentJSContext()); XPCJSRuntime::Get()->GetJSContextStack()->Pop(); mScx = nullptr; } bool danger::AutoCxPusher::IsStackTop() const { uint32_t currentDepth = XPCJSRuntime::Get()->GetJSContextStack()->Count(); MOZ_ASSERT(currentDepth >= mStackDepthAfterPush); return currentDepth == mStackDepthAfterPush; } } // namespace dom
bool ScriptSettingsInitialized() { return sScriptSettingsTLS.initialized(); }
void DestroyScriptSettings() { MOZ_ASSERT(sScriptSettingsTLS.get() == nullptr); }
GrGLvoid glClear_mozilla(GrGLbitfield mask) { return sGLContext.get()->fClear(mask); }
NS_IMETHOD Run() { TableTicker *t = tlsTicker.get(); // Pause the profiler during saving. // This will prevent us from recording sampling // regarding profile saving. This will also // prevent bugs caused by the circular buffer not // being thread safe. Bug 750989. t->SetPaused(true); // Get file path nsCOMPtr<nsIFile> tmpFile; nsCAutoString tmpPath; if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)))) { LOG("Failed to find temporary directory."); return NS_ERROR_FAILURE; } tmpPath.AppendPrintf("profile_%i_%i.txt", XRE_GetProcessType(), getpid()); nsresult rv = tmpFile->AppendNative(tmpPath); if (NS_FAILED(rv)) return rv; rv = tmpFile->GetNativePath(tmpPath); if (NS_FAILED(rv)) return rv; // Create a JSContext to run a JSObjectBuilder :( // Based on XPCShellEnvironment JSRuntime *rt; JSContext *cx; nsCOMPtr<nsIJSRuntimeService> rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1"); if (!rtsvc || NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) { LOG("failed to get RuntimeService"); return NS_ERROR_FAILURE;; } cx = JS_NewContext(rt, 8192); if (!cx) { LOG("Failed to get context"); return NS_ERROR_FAILURE; } { JSAutoRequest ar(cx); static JSClass c = { "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub }; JSObject *obj = JS_NewGlobalObject(cx, &c, NULL); std::ofstream stream; stream.open(tmpPath.get()); // Pause the profiler during saving. // This will prevent us from recording sampling // regarding profile saving. This will also // prevent bugs caused by the circular buffer not // being thread safe. Bug 750989. t->SetPaused(true); if (stream.is_open()) { JSAutoEnterCompartment autoComp; if (autoComp.enter(cx, obj)) { JSObject* profileObj = mozilla_sampler_get_profile_data(cx); jsval val = OBJECT_TO_JSVAL(profileObj); JS_Stringify(cx, &val, nsnull, JSVAL_NULL, WriteCallback, &stream); } else { LOG("Failed to enter compartment"); } stream.close(); LOGF("Saved to %s", tmpPath.get()); } else { LOG("Fail to open profile log file."); } } JS_EndRequest(cx); JS_DestroyContext(cx); t->SetPaused(false); return NS_OK; }
GrGLvoid glClearColor_mozilla(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) { return sGLContext.get()->fClearColor(red, green, blue, alpha); }
void mozilla_sampler_init(void* stackTop) { sInitCount++; if (stack_key_initialized) return; #ifdef SPS_STANDALONE mozilla::TimeStamp::Startup(); #endif LOG("BEGIN mozilla_sampler_init"); if (!tlsPseudoStack.init() || !tlsTicker.init() || !tlsStackTop.init()) { LOG("Failed to init."); return; } stack_key_initialized = true; Sampler::Startup(); PseudoStack *stack = PseudoStack::create(); tlsPseudoStack.set(stack); bool isMainThread = true; Sampler::RegisterCurrentThread(isMainThread ? gGeckoThreadName : "Application Thread", stack, isMainThread, stackTop); // Read interval settings from MOZ_PROFILER_INTERVAL and stack-scan // threshhold from MOZ_PROFILER_STACK_SCAN. read_profiler_env_vars(); // platform specific initialization OS::Startup(); #ifndef SPS_STANDALONE set_stderr_callback(mozilla_sampler_log); #endif // We can't open pref so we use an environment variable // to know if we should trigger the profiler on startup // NOTE: Default const char *val = getenv("MOZ_PROFILER_STARTUP"); if (!val || !*val) { return; } const char* features[] = {"js" , "leaf" , "threads" #if defined(XP_WIN) || defined(XP_MACOSX) \ || (defined(SPS_ARCH_arm) && defined(linux)) \ || defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux) , "stackwalk" #endif #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) , "java" #endif }; const char* threadFilters[] = { "GeckoMain", "Compositor" }; profiler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL, features, MOZ_ARRAY_LENGTH(features), threadFilters, MOZ_ARRAY_LENGTH(threadFilters)); LOG("END mozilla_sampler_init"); }
GrGLvoid glColorMask_mozilla(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha) { return sGLContext.get()->fColorMask(red, green, blue, alpha); }
// Values are only honored on the first start void mozilla_sampler_start(int aProfileEntries, double aInterval, const char** aFeatures, uint32_t aFeatureCount, const char** aThreadNameFilters, uint32_t aFilterCount) { LOG("BEGIN mozilla_sampler_start"); if (!stack_key_initialized) profiler_init(nullptr); /* If the sampling interval was set using env vars, use that in preference to anything else. */ if (sUnwindInterval > 0) aInterval = sUnwindInterval; /* If the entry count was set using env vars, use that, too: */ if (sProfileEntries > 0) aProfileEntries = sProfileEntries; // Reset the current state if the profiler is running profiler_stop(); TableTicker* t; t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL, aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount); tlsTicker.set(t); t->Start(); if (t->ProfileJS() || t->InPrivacyMode()) { ::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); std::vector<ThreadInfo*> threads = t->GetRegisteredThreads(); for (uint32_t i = 0; i < threads.size(); i++) { ThreadInfo* info = threads[i]; if (info->IsPendingDelete()) { continue; } ThreadProfile* thread_profile = info->Profile(); if (!thread_profile) { continue; } thread_profile->GetPseudoStack()->reinitializeOnResume(); #ifndef SPS_STANDALONE if (t->ProfileJS()) { thread_profile->GetPseudoStack()->enableJSSampling(); } if (t->InPrivacyMode()) { thread_profile->GetPseudoStack()->mPrivacyMode = true; } #endif } } #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) if (t->ProfileJava()) { int javaInterval = aInterval; // Java sampling doesn't accuratly keep up with 1ms sampling if (javaInterval < 10) { aInterval = 10; } mozilla::widget::GeckoJavaSampler::StartJavaProfiling(javaInterval, 1000); } #endif #ifndef SPS_STANDALONE if (t->AddMainThreadIO()) { if (!sInterposeObserver) { // Lazily create IO interposer observer sInterposeObserver = new mozilla::ProfilerIOInterposeObserver(); } mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll, sInterposeObserver); } #endif sIsProfiling = true; #ifndef SPS_STANDALONE sIsGPUProfiling = t->ProfileGPU(); sIsLayersDump = t->LayersDump(); sIsDisplayListDump = t->DisplayListDump(); sIsRestyleProfiling = t->ProfileRestyle(); if (Sampler::CanNotifyObservers()) { nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) { nsTArray<nsCString> featuresArray; nsTArray<nsCString> threadNameFiltersArray; for (size_t i = 0; i < aFeatureCount; ++i) { featuresArray.AppendElement(aFeatures[i]); } for (size_t i = 0; i < aFilterCount; ++i) { threadNameFiltersArray.AppendElement(aThreadNameFilters[i]); } nsCOMPtr<nsIProfilerStartParams> params = new nsProfilerStartParams(aProfileEntries, aInterval, featuresArray, threadNameFiltersArray); os->NotifyObservers(params, "profiler-started", nullptr); } } #endif LOG("END mozilla_sampler_start"); }
GrGLvoid glCopyTexSubImage2D_mozilla(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) { return sGLContext.get()->fCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); }
namespace dom { class ScriptSettingsStack; static mozilla::ThreadLocal<ScriptSettingsStack*> sScriptSettingsTLS; ScriptSettingsStackEntry ScriptSettingsStackEntry::NoJSAPISingleton; class ScriptSettingsStack { public: static ScriptSettingsStack& Ref() { return *sScriptSettingsTLS.get(); } ScriptSettingsStack() {}; void Push(ScriptSettingsStackEntry* aSettings) { // The bottom-most entry must always be a candidate entry point. MOZ_ASSERT_IF(mStack.Length() == 0 || mStack.LastElement()->NoJSAPI(), aSettings->mIsCandidateEntryPoint); mStack.AppendElement(aSettings); } void PushNoJSAPI() { mStack.AppendElement(&ScriptSettingsStackEntry::NoJSAPISingleton); } void Pop() { MOZ_ASSERT(mStack.Length() > 0); mStack.RemoveElementAt(mStack.Length() - 1); } ScriptSettingsStackEntry* Incumbent() { if (!mStack.Length()) { return nullptr; } return mStack.LastElement(); } nsIGlobalObject* IncumbentGlobal() { ScriptSettingsStackEntry *entry = Incumbent(); return entry ? entry->mGlobalObject : nullptr; } ScriptSettingsStackEntry* EntryPoint() { if (!mStack.Length()) return nullptr; for (int i = mStack.Length() - 1; i >= 0; --i) { if (mStack[i]->mIsCandidateEntryPoint) { return mStack[i]; } } MOZ_CRASH("Non-empty stack should always have an entry point"); } nsIGlobalObject* EntryGlobal() { ScriptSettingsStackEntry *entry = EntryPoint(); return entry ? entry->mGlobalObject : nullptr; } private: // These pointers are caller-owned. nsTArray<ScriptSettingsStackEntry*> mStack; }; void InitScriptSettings() { if (!sScriptSettingsTLS.initialized()) { bool success = sScriptSettingsTLS.init(); if (!success) { MOZ_CRASH(); } } ScriptSettingsStack* ptr = new ScriptSettingsStack(); sScriptSettingsTLS.set(ptr); } void DestroyScriptSettings() { ScriptSettingsStack* ptr = sScriptSettingsTLS.get(); MOZ_ASSERT(ptr); sScriptSettingsTLS.set(nullptr); delete ptr; } // This mostly gets the entry global, but doesn't entirely match the spec in // certain edge cases. It's good enough for some purposes, but not others. If // you want to call this function, ping bholley and describe your use-case. nsIGlobalObject* BrokenGetEntryGlobal() { // We need the current JSContext in order to check the JS for // scripted frames that may have appeared since anyone last // manipulated the stack. If it's null, that means that there // must be no entry global on the stack. JSContext *cx = nsContentUtils::GetCurrentJSContextForThread(); if (!cx) { MOZ_ASSERT(ScriptSettingsStack::Ref().EntryGlobal() == nullptr); return nullptr; } return nsJSUtils::GetDynamicScriptGlobal(cx); } // Note: When we're ready to expose it, GetEntryGlobal will look similar to // GetIncumbentGlobal below. nsIGlobalObject* GetIncumbentGlobal() { // We need the current JSContext in order to check the JS for // scripted frames that may have appeared since anyone last // manipulated the stack. If it's null, that means that there // must be no entry global on the stack, and therefore no incumbent // global either. JSContext *cx = nsContentUtils::GetCurrentJSContextForThread(); if (!cx) { MOZ_ASSERT(ScriptSettingsStack::Ref().EntryGlobal() == nullptr); return nullptr; } // See what the JS engine has to say. If we've got a scripted caller // override in place, the JS engine will lie to us and pretend that // there's nothing on the JS stack, which will cause us to check the // incumbent script stack below. if (JSObject *global = JS::GetScriptedCallerGlobal(cx)) { return xpc::GetNativeForGlobal(global); } // Ok, nothing from the JS engine. Let's use whatever's on the // explicit stack. return ScriptSettingsStack::Ref().IncumbentGlobal(); } nsIPrincipal* GetWebIDLCallerPrincipal() { MOZ_ASSERT(NS_IsMainThread()); ScriptSettingsStackEntry *entry = ScriptSettingsStack::Ref().EntryPoint(); // If we have an entry point that is not the NoJSAPI singleton, we know it // must be an AutoEntryScript. if (!entry || entry->NoJSAPI()) { return nullptr; } AutoEntryScript* aes = static_cast<AutoEntryScript*>(entry); // We can't yet rely on the Script Settings Stack to properly determine the // entry script, because there are still lots of places in the tree where we // don't yet use an AutoEntryScript (bug 951991 tracks this work). In the // mean time though, we can make some observations to hack around the // problem: // // (1) All calls into JS-implemented WebIDL go through CallSetup, which goes // through AutoEntryScript. // (2) The top candidate entry point in the Script Settings Stack is the // entry point if and only if no other JSContexts have been pushed on // top of the push made by that entry's AutoEntryScript. // // Because of (1), all of the cases where we might return a non-null // WebIDL Caller are guaranteed to have put an entry on the Script Settings // Stack, so we can restrict our search to that. Moreover, (2) gives us a // criterion to determine whether an entry in the Script Setting Stack means // that we should return a non-null WebIDL Caller. // // Once we fix bug 951991, this can all be simplified. if (!aes->CxPusherIsStackTop()) { return nullptr; } return aes->mWebIDLCallerPrincipal; } static JSContext* FindJSContext(nsIGlobalObject* aGlobalObject) { MOZ_ASSERT(NS_IsMainThread()); JSContext *cx = nullptr; nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobalObject); if (sgo && sgo->GetScriptContext()) { cx = sgo->GetScriptContext()->GetNativeContext(); } if (!cx) { cx = nsContentUtils::GetSafeJSContext(); } return cx; } AutoJSAPI::AutoJSAPI() : mCx(nsContentUtils::GetDefaultJSContextForThread()) { if (NS_IsMainThread()) { mCxPusher.construct(mCx); } // Leave the cx in a null compartment. mNullAc.construct(mCx); } AutoJSAPI::AutoJSAPI(JSContext *aCx, bool aIsMainThread, bool aSkipNullAc) : mCx(aCx) { MOZ_ASSERT_IF(aIsMainThread, NS_IsMainThread()); if (aIsMainThread) { mCxPusher.construct(mCx); } // In general we want to leave the cx in a null compartment, but we let // subclasses skip this if they plan to immediately enter a compartment. if (!aSkipNullAc) { mNullAc.construct(mCx); } } AutoJSAPIWithErrorsReportedToWindow::AutoJSAPIWithErrorsReportedToWindow(nsIScriptContext* aScx) : AutoJSAPI(aScx->GetNativeContext(), /* aIsMainThread = */ true) { } AutoJSAPIWithErrorsReportedToWindow::AutoJSAPIWithErrorsReportedToWindow(nsIGlobalObject* aGlobalObject) : AutoJSAPI(FindJSContext(aGlobalObject), /* aIsMainThread = */ true) { } AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject, bool aIsMainThread, JSContext* aCx) : AutoJSAPI(aCx ? aCx : FindJSContext(aGlobalObject), aIsMainThread, /* aSkipNullAc = */ true) , ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true) , mAc(cx(), aGlobalObject->GetGlobalJSObject()) , mStack(ScriptSettingsStack::Ref()) , mWebIDLCallerPrincipal(nullptr) { MOZ_ASSERT(aGlobalObject); MOZ_ASSERT_IF(!aCx, aIsMainThread); // cx is mandatory off-main-thread. MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == FindJSContext(aGlobalObject)); mStack.Push(this); } AutoEntryScript::~AutoEntryScript() { MOZ_ASSERT(mStack.Incumbent() == this); mStack.Pop(); } AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject) : ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ false) , mStack(ScriptSettingsStack::Ref()) , mCallerOverride(nsContentUtils::GetCurrentJSContextForThread()) { mStack.Push(this); } AutoIncumbentScript::~AutoIncumbentScript() { MOZ_ASSERT(mStack.Incumbent() == this); mStack.Pop(); } AutoNoJSAPI::AutoNoJSAPI(bool aIsMainThread) : mStack(ScriptSettingsStack::Ref()) { MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContextForThread(), !JS_IsExceptionPending(nsContentUtils::GetCurrentJSContextForThread())); if (aIsMainThread) { mCxPusher.construct(static_cast<JSContext*>(nullptr), /* aAllowNull = */ true); } mStack.PushNoJSAPI(); } AutoNoJSAPI::~AutoNoJSAPI() { mStack.Pop(); } } // namespace dom
GrGLuint glCreateShader_mozilla(GrGLenum type) { return sGLContext.get()->fCreateShader(type); }
static ScriptSettingsStack& Ref() { return *sScriptSettingsTLS.get(); }
GrGLvoid glDeleteFramebuffers_mozilla(GrGLsizei n, const GrGLuint* framebuffers) { return sGLContext.get()->fDeleteFramebuffers(n, framebuffers); }
GrGLvoid glBufferSubData_mozilla(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, const void* data) { return sGLContext.get()->fBufferSubData(target, offset, size, data); }
GrGLvoid glDeleteRenderbuffers_mozilla(GrGLsizei n, const GrGLuint* renderbuffers) { return sGLContext.get()->fDeleteRenderbuffers(n, renderbuffers); }
GrGLenum glCheckFramebufferStatus_mozilla(GrGLenum target) { return sGLContext.get()->fCheckFramebufferStatus(target); }
GrGLvoid glDeleteTextures_mozilla(GrGLsizei n, const GrGLuint* textures) { return sGLContext.get()->fDeleteTextures(n, textures); }
GrGLvoid glBufferData_mozilla(GrGLenum target, GrGLsizeiptr size, const void* data, GrGLenum usage) { return sGLContext.get()->fBufferData(target, size, data, usage); }