/* -*- 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 Create(JSContext* aCx, nsPIDOMWindowInner* aWindow, ErrorResult& aRv); static already_AddRefed 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& aData); MOZ_CAN_RUN_SCRIPT static void Info(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void Warn(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void Error(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void Exception(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void Debug(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void Table(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void Trace(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void Dir(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void Dirxml(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void Group(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void GroupCollapsed(const GlobalObject& aGlobal, const Sequence& 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& 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 aData); MOZ_CAN_RUN_SCRIPT static void Profile(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void ProfileEnd(const GlobalObject& aGlobal, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void Assert(const GlobalObject& aGlobal, bool aCondition, const Sequence& 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 CreateInstance( const GlobalObject& aGlobal, const ConsoleInstanceOptions& aOptions); void ClearStorage(); void RetrieveConsoleEvents(JSContext* aCx, nsTArray& 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 GetConsole(const GlobalObject& aGlobal); static already_AddRefed GetConsoleInternal( const GlobalObject& aGlobal, ErrorResult& aRv); MOZ_CAN_RUN_SCRIPT static void ProfileMethod(const GlobalObject& aGlobal, MethodName aName, const nsAString& aAction, const Sequence& aData); MOZ_CAN_RUN_SCRIPT void ProfileMethodInternal(JSContext* aCx, MethodName aName, const nsAString& aAction, const Sequence& 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& aData); MOZ_CAN_RUN_SCRIPT static void Method(const GlobalObject& aGlobal, MethodName aName, const nsAString& aString, const Sequence& aData); MOZ_CAN_RUN_SCRIPT void MethodInternal(JSContext* aCx, MethodName aName, const nsAString& aString, const Sequence& aData); MOZ_CAN_RUN_SCRIPT static void StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel, const Sequence& aData, MethodName aMethodName, const nsAString& aMethodString); MOZ_CAN_RUN_SCRIPT void StringMethodInternal(JSContext* aCx, const nsAString& aLabel, const Sequence& aData, MethodName aMethodName, const nsAString& aMethodString); MainThreadConsoleData* GetOrCreateMainThreadData(); // Returns true on success; otherwise false. bool StoreCallData(JSContext* aCx, ConsoleCallData* aCallData, const Sequence& 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& 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& aArguments, JS::Handle aTargetScope, JS::MutableHandle aEventValue, ConsoleCallData* aData, nsTArray* 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& aData, Sequence& 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& 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& aData, nsAString& aCountLabel); static bool ShouldIncludeStackTrace(MethodName aMethodName); void AssertIsOnOwningThread() const; bool IsShuttingDown() const; bool MonotonicTimer(JSContext* aCx, MethodName aMethodName, const Sequence& aData, DOMHighResTimeStamp* aTimeStamp); MOZ_CAN_RUN_SCRIPT void MaybeExecuteDumpFunction(JSContext* aCx, const nsAString& aMethodName, const Sequence& 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& aArguments); void Trace(const TraceCallbacks& aCallbacks, void* aClosure); bool PopulateArgumentsSequence(Sequence& aSequence) const; JSObject* Global() const { return mGlobal; } private: void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(ArgumentData); } NS_DECL_OWNINGTHREAD; JS::Heap mGlobal; nsTArray> mArguments; }; // Owning/CC thread only nsCOMPtr mGlobal; // Touched on the owner thread. nsTHashMap mTimerRegistry; nsTHashMap mCounterRegistry; nsTArray> mCallDataStorage; // These are references to the arguments we received in each call // from the DOM bindings. // Vector 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 mArgumentStorage; RefPtr mConsoleEventNotifier; RefPtr mMainThreadData; // This is the stack for grouping relating to Console-thread events, when // the Console thread is not the main thread. nsTArray mGroupStack; uint64_t mOuterID; uint64_t mInnerID; // Set only by ConsoleInstance: nsString mConsoleID; nsString mPassedInnerID; RefPtr 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 */