diff options
Diffstat (limited to '')
-rw-r--r-- | dom/console/Console.h | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/dom/console/Console.h b/dom/console/Console.h new file mode 100644 index 0000000000..c7a5590467 --- /dev/null +++ b/dom/console/Console.h @@ -0,0 +1,450 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_Console_h +#define mozilla_dom_Console_h + +#include "domstubs.h" +#include "mozilla/dom/ConsoleBinding.h" +#include "mozilla/TimeStamp.h" +#include "nsCycleCollectionParticipant.h" +#include "nsTHashMap.h" +#include "nsHashKeys.h" +#include "nsIObserver.h" +#include "nsWeakReference.h" + +class nsIConsoleAPIStorage; +class nsIGlobalObject; +class nsPIDOMWindowInner; +class nsIStackFrame; + +namespace mozilla::dom { + +class AnyCallback; +class ConsoleCallData; +class ConsoleInstance; +class ConsoleRunnable; +class ConsoleCallDataRunnable; +class ConsoleProfileRunnable; +class MainThreadConsoleData; + +class Console final : public nsIObserver, public nsSupportsWeakReference { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Console, nsIObserver) + NS_DECL_NSIOBSERVER + + static already_AddRefed<Console> Create(JSContext* aCx, + nsPIDOMWindowInner* aWindow, + ErrorResult& aRv); + + static already_AddRefed<Console> CreateForWorklet(JSContext* aCx, + nsIGlobalObject* aGlobal, + uint64_t aOuterWindowID, + uint64_t aInnerWindowID, + ErrorResult& aRv); + + MOZ_CAN_RUN_SCRIPT + static void Log(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Info(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Warn(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Error(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Exception(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Debug(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Table(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Trace(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Dir(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Dirxml(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Group(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void GroupCollapsed(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void GroupEnd(const GlobalObject& aGlobal); + + MOZ_CAN_RUN_SCRIPT + static void Time(const GlobalObject& aGlobal, const nsAString& aLabel); + + MOZ_CAN_RUN_SCRIPT + static void TimeLog(const GlobalObject& aGlobal, const nsAString& aLabel, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void TimeEnd(const GlobalObject& aGlobal, const nsAString& aLabel); + + MOZ_CAN_RUN_SCRIPT + static void TimeStamp(const GlobalObject& aGlobal, + const JS::Handle<JS::Value> aData); + + MOZ_CAN_RUN_SCRIPT + static void Profile(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void ProfileEnd(const GlobalObject& aGlobal, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Assert(const GlobalObject& aGlobal, bool aCondition, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Count(const GlobalObject& aGlobal, const nsAString& aLabel); + + MOZ_CAN_RUN_SCRIPT + static void CountReset(const GlobalObject& aGlobal, const nsAString& aLabel); + + MOZ_CAN_RUN_SCRIPT + static void Clear(const GlobalObject& aGlobal); + + static already_AddRefed<ConsoleInstance> CreateInstance( + const GlobalObject& aGlobal, const ConsoleInstanceOptions& aOptions); + + void ClearStorage(); + + void RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents, + ErrorResult& aRv); + + void SetConsoleEventHandler(AnyCallback* aHandler); + + private: + Console(JSContext* aCx, nsIGlobalObject* aGlobal, uint64_t aOuterWindowID, + uint64_t aInnerWIndowID); + ~Console(); + + void Initialize(ErrorResult& aRv); + + void Shutdown(); + + enum MethodName { + MethodLog, + MethodInfo, + MethodWarn, + MethodError, + MethodException, + MethodDebug, + MethodTable, + MethodTrace, + MethodDir, + MethodDirxml, + MethodGroup, + MethodGroupCollapsed, + MethodGroupEnd, + MethodTime, + MethodTimeLog, + MethodTimeEnd, + MethodTimeStamp, + MethodAssert, + MethodCount, + MethodCountReset, + MethodClear, + MethodProfile, + MethodProfileEnd, + }; + + static already_AddRefed<Console> GetConsole(const GlobalObject& aGlobal); + + static already_AddRefed<Console> GetConsoleInternal( + const GlobalObject& aGlobal, ErrorResult& aRv); + + MOZ_CAN_RUN_SCRIPT + static void ProfileMethod(const GlobalObject& aGlobal, MethodName aName, + const nsAString& aAction, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + void ProfileMethodInternal(JSContext* aCx, MethodName aName, + const nsAString& aAction, + const Sequence<JS::Value>& aData); + + // Implementation of the mainthread-only parts of ProfileMethod. + // This is indepedent of console instance state. + static void ProfileMethodMainthread(JSContext* aCx, const nsAString& aAction, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void Method(const GlobalObject& aGlobal, MethodName aName, + const nsAString& aString, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + void MethodInternal(JSContext* aCx, MethodName aName, + const nsAString& aString, + const Sequence<JS::Value>& aData); + + MOZ_CAN_RUN_SCRIPT + static void StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel, + const Sequence<JS::Value>& aData, + MethodName aMethodName, + const nsAString& aMethodString); + + MOZ_CAN_RUN_SCRIPT + void StringMethodInternal(JSContext* aCx, const nsAString& aLabel, + const Sequence<JS::Value>& aData, + MethodName aMethodName, + const nsAString& aMethodString); + + MainThreadConsoleData* GetOrCreateMainThreadData(); + + // Returns true on success; otherwise false. + bool StoreCallData(JSContext* aCx, ConsoleCallData* aCallData, + const Sequence<JS::Value>& aArguments); + + void UnstoreCallData(ConsoleCallData* aData); + + // aCx and aArguments must be in the same JS compartment. + MOZ_CAN_RUN_SCRIPT + void NotifyHandler(JSContext* aCx, const Sequence<JS::Value>& aArguments, + ConsoleCallData* aData); + + // PopulateConsoleNotificationInTheTargetScope receives aCx and aArguments in + // the same JS compartment and populates the ConsoleEvent object + // (aEventValue) in the aTargetScope. + // aTargetScope can be: + // - the system-principal scope when we want to dispatch the ConsoleEvent to + // nsIConsoleAPIStorage (See the comment in Console.cpp about the use of + // xpc::PrivilegedJunkScope() + // - the mConsoleEventNotifier->CallableGlobal() when we want to notify this + // handler about a new ConsoleEvent. + // - It can be the global from the JSContext when RetrieveConsoleEvents is + // called. + static bool PopulateConsoleNotificationInTheTargetScope( + JSContext* aCx, const Sequence<JS::Value>& aArguments, + JS::Handle<JSObject*> aTargetScope, + JS::MutableHandle<JS::Value> aEventValue, ConsoleCallData* aData, + nsTArray<nsString>* aGroupStack); + + enum TimerStatus { + eTimerUnknown, + eTimerDone, + eTimerAlreadyExists, + eTimerDoesntExist, + eTimerJSException, + eTimerMaxReached, + }; + + static JS::Value CreateTimerError(JSContext* aCx, const nsAString& aLabel, + TimerStatus aStatus); + + // StartTimer is called on the owning thread and populates aTimerLabel and + // aTimerValue. + // * aCx - the JSContext rooting aName. + // * aName - this is (should be) the name of the timer as JS::Value. + // * aTimestamp - the monotonicTimer for this context taken from + // performance.now(). + // * aTimerLabel - This label will be populated with the aName converted to a + // string. + // * aTimerValue - the StartTimer value stored into (or taken from) + // mTimerRegistry. + TimerStatus StartTimer(JSContext* aCx, const JS::Value& aName, + DOMHighResTimeStamp aTimestamp, nsAString& aTimerLabel, + DOMHighResTimeStamp* aTimerValue); + + // CreateStartTimerValue generates a ConsoleTimerStart dictionary exposed as + // JS::Value. If aTimerStatus is false, it generates a ConsoleTimerError + // instead. It's called only after the execution StartTimer on the owning + // thread. + // * aCx - this is the context that will root the returned value. + // * aTimerLabel - this label must be what StartTimer received as aTimerLabel. + // * aTimerStatus - the return value of StartTimer. + static JS::Value CreateStartTimerValue(JSContext* aCx, + const nsAString& aTimerLabel, + TimerStatus aTimerStatus); + + // LogTimer follows the same pattern as StartTimer: it runs on the + // owning thread and populates aTimerLabel and aTimerDuration, used by + // CreateLogOrEndTimerValue. + // * aCx - the JSContext rooting aName. + // * aName - this is (should be) the name of the timer as JS::Value. + // * aTimestamp - the monotonicTimer for this context taken from + // performance.now(). + // * aTimerLabel - This label will be populated with the aName converted to a + // string. + // * aTimerDuration - the difference between aTimestamp and when the timer + // started (see StartTimer). + // * aCancelTimer - if true, the timer is removed from the table. + TimerStatus LogTimer(JSContext* aCx, const JS::Value& aName, + DOMHighResTimeStamp aTimestamp, nsAString& aTimerLabel, + double* aTimerDuration, bool aCancelTimer); + + // This method generates a ConsoleTimerEnd dictionary exposed as JS::Value, or + // a ConsoleTimerError dictionary if aTimerStatus is false. See LogTimer. + // * aCx - this is the context that will root the returned value. + // * aTimerLabel - this label must be what LogTimer received as aTimerLabel. + // * aTimerDuration - this is what LogTimer received as aTimerDuration + // * aTimerStatus - the return value of LogTimer. + static JS::Value CreateLogOrEndTimerValue(JSContext* aCx, + const nsAString& aLabel, + double aDuration, + TimerStatus aStatus); + + // The method populates a Sequence from an array of JS::Value. + bool ArgumentsToValueList(const Sequence<JS::Value>& aData, + Sequence<JS::Value>& aSequence) const; + + // This method follows the same pattern as StartTimer: its runs on the owning + // thread and populate aCountLabel, used by CreateCounterOrResetCounterValue. + // Returns 3 possible values: + // * MAX_PAGE_COUNTERS in case of error that has to be reported; + // * 0 in case of a CX exception. The operation cannot continue; + // * the incremented counter value. + // Params: + // * aCx - the JSContext rooting aData. + // * aData - the arguments received by the console.count() method. + // * aCountLabel - the label that will be populated by this method. + uint32_t IncreaseCounter(JSContext* aCx, const Sequence<JS::Value>& aData, + nsAString& aCountLabel); + + // This method follows the same pattern as StartTimer: its runs on the owning + // thread and populate aCountLabel, used by CreateCounterResetValue. Returns + // 3 possible values: + // * MAX_PAGE_COUNTERS in case of error that has to be reported; + // * 0 elsewhere. In case of a CX exception, aCountLabel will be an empty + // string. + // Params: + // * aCx - the JSContext rooting aData. + // * aData - the arguments received by the console.count() method. + // * aCountLabel - the label that will be populated by this method. + uint32_t ResetCounter(JSContext* aCx, const Sequence<JS::Value>& aData, + nsAString& aCountLabel); + + static bool ShouldIncludeStackTrace(MethodName aMethodName); + + void AssertIsOnOwningThread() const; + + bool IsShuttingDown() const; + + bool MonotonicTimer(JSContext* aCx, MethodName aMethodName, + const Sequence<JS::Value>& aData, + DOMHighResTimeStamp* aTimeStamp); + + void StringifyElement(Element* aElement, nsAString& aOut); + + MOZ_CAN_RUN_SCRIPT + void MaybeExecuteDumpFunction(JSContext* aCx, const nsAString& aMethodName, + const Sequence<JS::Value>& aData, + nsIStackFrame* aStack); + + MOZ_CAN_RUN_SCRIPT + void MaybeExecuteDumpFunctionForTime(JSContext* aCx, MethodName aMethodName, + const nsAString& aMethodString, + uint64_t aMonotonicTimer, + const JS::Value& aData); + + MOZ_CAN_RUN_SCRIPT + void ExecuteDumpFunction(const nsAString& aMessage); + + bool ShouldProceed(MethodName aName) const; + + uint32_t WebIDLLogLevelToInteger(ConsoleLogLevel aLevel) const; + + uint32_t InternalLogLevelToInteger(MethodName aName) const; + + class ArgumentData { + public: + bool Initialize(JSContext* aCx, const Sequence<JS::Value>& aArguments); + void Trace(const TraceCallbacks& aCallbacks, void* aClosure); + bool PopulateArgumentsSequence(Sequence<JS::Value>& aSequence) const; + JSObject* Global() const { return mGlobal; } + + private: + void AssertIsOnOwningThread() const { + NS_ASSERT_OWNINGTHREAD(ArgumentData); + } + + NS_DECL_OWNINGTHREAD; + JS::Heap<JSObject*> mGlobal; + nsTArray<JS::Heap<JS::Value>> mArguments; + }; + + // Owning/CC thread only + nsCOMPtr<nsIGlobalObject> mGlobal; + + // Touched on the owner thread. + nsTHashMap<nsStringHashKey, DOMHighResTimeStamp> mTimerRegistry; + nsTHashMap<nsStringHashKey, uint32_t> mCounterRegistry; + + nsTArray<RefPtr<ConsoleCallData>> mCallDataStorage; + // These are references to the arguments we received in each call + // from the DOM bindings. + // Vector<T> supports non-memmovable types such as ArgumentData + // (without any need to jump through hoops like + // MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR_FOR_TEMPLATE for nsTArray). + Vector<ArgumentData> mArgumentStorage; + + RefPtr<AnyCallback> mConsoleEventNotifier; + + RefPtr<MainThreadConsoleData> mMainThreadData; + // This is the stack for grouping relating to Console-thread events, when + // the Console thread is not the main thread. + nsTArray<nsString> mGroupStack; + + uint64_t mOuterID; + uint64_t mInnerID; + + // Set only by ConsoleInstance: + nsString mConsoleID; + nsString mPassedInnerID; + RefPtr<ConsoleInstanceDumpCallback> mDumpFunction; + bool mDumpToStdout; + nsString mPrefix; + bool mChromeInstance; + ConsoleLogLevel mMaxLogLevel; + nsString mMaxLogLevelPref; + + enum { eUnknown, eInitialized, eShuttingDown } mStatus; + + // This is used when Console is created and it's used only for JSM custom + // console instance. + mozilla::TimeStamp mCreationTimeStamp; + + friend class ConsoleCallData; + friend class ConsoleCallDataWorkletRunnable; + friend class ConsoleInstance; + friend class ConsoleProfileWorkerRunnable; + friend class ConsoleProfileWorkletRunnable; + friend class ConsoleRunnable; + friend class ConsoleWorkerRunnable; + friend class ConsoleWorkletRunnable; + friend class MainThreadConsoleData; +}; + +} // namespace mozilla::dom + +#endif /* mozilla_dom_Console_h */ |