Console.h (17243B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_Console_h 8 #define mozilla_dom_Console_h 9 10 #include "domstubs.h" 11 #include "mozilla/TimeStamp.h" 12 #include "mozilla/dom/ConsoleBinding.h" 13 #include "mozilla/dom/ConsoleInstanceBinding.h" 14 #include "nsCycleCollectionParticipant.h" 15 #include "nsHashKeys.h" 16 #include "nsIObserver.h" 17 #include "nsTHashMap.h" 18 #include "nsWeakReference.h" 19 20 class nsIConsoleAPIStorage; 21 class nsIGlobalObject; 22 class nsPIDOMWindowInner; 23 class nsIStackFrame; 24 25 namespace mozilla::dom { 26 27 class AnyCallback; 28 class ConsoleCallData; 29 class ConsoleInstance; 30 class ConsoleRunnable; 31 class ConsoleCallDataRunnable; 32 class ConsoleProfileRunnable; 33 class MainThreadConsoleData; 34 35 class Console final : public nsIObserver, public nsSupportsWeakReference { 36 public: 37 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 38 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Console, nsIObserver) 39 NS_DECL_NSIOBSERVER 40 41 static already_AddRefed<Console> Create(JSContext* aCx, 42 nsPIDOMWindowInner* aWindow, 43 ErrorResult& aRv); 44 45 static already_AddRefed<Console> CreateForWorklet(JSContext* aCx, 46 nsIGlobalObject* aGlobal, 47 uint64_t aOuterWindowID, 48 uint64_t aInnerWindowID, 49 ErrorResult& aRv); 50 51 MOZ_CAN_RUN_SCRIPT 52 static void Log(const GlobalObject& aGlobal, 53 const Sequence<JS::Value>& aData); 54 55 MOZ_CAN_RUN_SCRIPT 56 static void Info(const GlobalObject& aGlobal, 57 const Sequence<JS::Value>& aData); 58 59 MOZ_CAN_RUN_SCRIPT 60 static void Warn(const GlobalObject& aGlobal, 61 const Sequence<JS::Value>& aData); 62 63 MOZ_CAN_RUN_SCRIPT 64 static void Error(const GlobalObject& aGlobal, 65 const Sequence<JS::Value>& aData); 66 67 MOZ_CAN_RUN_SCRIPT 68 static void Exception(const GlobalObject& aGlobal, 69 const Sequence<JS::Value>& aData); 70 71 MOZ_CAN_RUN_SCRIPT 72 static void Debug(const GlobalObject& aGlobal, 73 const Sequence<JS::Value>& aData); 74 75 MOZ_CAN_RUN_SCRIPT 76 static void Table(const GlobalObject& aGlobal, 77 const Sequence<JS::Value>& aData); 78 79 MOZ_CAN_RUN_SCRIPT 80 static void Trace(const GlobalObject& aGlobal, 81 const Sequence<JS::Value>& aData); 82 83 MOZ_CAN_RUN_SCRIPT 84 static void Dir(const GlobalObject& aGlobal, 85 const Sequence<JS::Value>& aData); 86 87 MOZ_CAN_RUN_SCRIPT 88 static void Dirxml(const GlobalObject& aGlobal, 89 const Sequence<JS::Value>& aData); 90 91 MOZ_CAN_RUN_SCRIPT 92 static void Group(const GlobalObject& aGlobal, 93 const Sequence<JS::Value>& aData); 94 95 MOZ_CAN_RUN_SCRIPT 96 static void GroupCollapsed(const GlobalObject& aGlobal, 97 const Sequence<JS::Value>& aData); 98 99 MOZ_CAN_RUN_SCRIPT 100 static void GroupEnd(const GlobalObject& aGlobal); 101 102 MOZ_CAN_RUN_SCRIPT 103 static void Time(const GlobalObject& aGlobal, const nsAString& aLabel); 104 105 MOZ_CAN_RUN_SCRIPT 106 static void TimeLog(const GlobalObject& aGlobal, const nsAString& aLabel, 107 const Sequence<JS::Value>& aData); 108 109 MOZ_CAN_RUN_SCRIPT 110 static void TimeEnd(const GlobalObject& aGlobal, const nsAString& aLabel); 111 112 MOZ_CAN_RUN_SCRIPT 113 static void TimeStamp(const GlobalObject& aGlobal, 114 const JS::Handle<JS::Value> aData); 115 116 MOZ_CAN_RUN_SCRIPT 117 static void Profile(const GlobalObject& aGlobal, 118 const Sequence<JS::Value>& aData); 119 120 MOZ_CAN_RUN_SCRIPT 121 static void ProfileEnd(const GlobalObject& aGlobal, 122 const Sequence<JS::Value>& aData); 123 124 MOZ_CAN_RUN_SCRIPT 125 static void Assert(const GlobalObject& aGlobal, bool aCondition, 126 const Sequence<JS::Value>& aData); 127 128 MOZ_CAN_RUN_SCRIPT 129 static void Count(const GlobalObject& aGlobal, const nsAString& aLabel); 130 131 MOZ_CAN_RUN_SCRIPT 132 static void CountReset(const GlobalObject& aGlobal, const nsAString& aLabel); 133 134 MOZ_CAN_RUN_SCRIPT 135 static void Clear(const GlobalObject& aGlobal); 136 137 MOZ_CAN_RUN_SCRIPT 138 static already_AddRefed<ConsoleInstance> CreateInstance( 139 const GlobalObject& aGlobal, const ConsoleInstanceOptions& aOptions); 140 141 void ClearStorage(); 142 143 void RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents, 144 ErrorResult& aRv); 145 146 void SetConsoleEventHandler(AnyCallback* aHandler); 147 148 private: 149 Console(JSContext* aCx, nsIGlobalObject* aGlobal, uint64_t aOuterWindowID, 150 uint64_t aInnerWIndowID, const nsAString& aPrefix = u""_ns); 151 ~Console(); 152 153 void Initialize(ErrorResult& aRv); 154 155 void Shutdown(); 156 157 enum MethodName { 158 MethodLog, 159 MethodInfo, 160 MethodWarn, 161 MethodError, 162 MethodException, 163 MethodDebug, 164 MethodTable, 165 MethodTrace, 166 MethodDir, 167 MethodDirxml, 168 MethodGroup, 169 MethodGroupCollapsed, 170 MethodGroupEnd, 171 MethodTime, 172 MethodTimeLog, 173 MethodTimeEnd, 174 MethodTimeStamp, 175 MethodAssert, 176 MethodCount, 177 MethodCountReset, 178 MethodClear, 179 MethodProfile, 180 MethodProfileEnd, 181 }; 182 183 static already_AddRefed<Console> GetConsole(const GlobalObject& aGlobal); 184 185 static already_AddRefed<Console> GetConsoleInternal( 186 const GlobalObject& aGlobal, ErrorResult& aRv); 187 188 MOZ_CAN_RUN_SCRIPT 189 static void ProfileMethod(const GlobalObject& aGlobal, MethodName aName, 190 const nsAString& aAction, 191 const Sequence<JS::Value>& aData); 192 193 MOZ_CAN_RUN_SCRIPT 194 void ProfileMethodInternal(JSContext* aCx, MethodName aName, 195 const nsAString& aAction, 196 const Sequence<JS::Value>& aData); 197 198 // Implementation of the mainthread-only parts of ProfileMethod. 199 // This is indepedent of console instance state. 200 static void ProfileMethodMainthread(JSContext* aCx, const nsAString& aAction, 201 const Sequence<JS::Value>& aData); 202 203 MOZ_CAN_RUN_SCRIPT 204 static void Method(const GlobalObject& aGlobal, MethodName aName, 205 const nsAString& aString, 206 const Sequence<JS::Value>& aData); 207 208 MOZ_CAN_RUN_SCRIPT 209 void MethodInternal(JSContext* aCx, MethodName aName, 210 const nsAString& aString, 211 const Sequence<JS::Value>& aData); 212 213 MOZ_CAN_RUN_SCRIPT 214 static void StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel, 215 const Sequence<JS::Value>& aData, 216 MethodName aMethodName, 217 const nsAString& aMethodString); 218 219 MOZ_CAN_RUN_SCRIPT 220 void StringMethodInternal(JSContext* aCx, const nsAString& aLabel, 221 const Sequence<JS::Value>& aData, 222 MethodName aMethodName, 223 const nsAString& aMethodString); 224 225 MainThreadConsoleData* GetOrCreateMainThreadData(); 226 227 // Returns true on success; otherwise false. 228 bool StoreCallData(JSContext* aCx, ConsoleCallData* aCallData, 229 const Sequence<JS::Value>& aArguments); 230 231 void UnstoreCallData(ConsoleCallData* aData); 232 233 // aCx and aArguments must be in the same JS compartment. 234 MOZ_CAN_RUN_SCRIPT 235 void NotifyHandler(JSContext* aCx, const Sequence<JS::Value>& aArguments, 236 ConsoleCallData* aData); 237 238 // PopulateConsoleNotificationInTheTargetScope receives aCx and aArguments in 239 // the same JS compartment and populates the ConsoleEvent object 240 // (aEventValue) in the aTargetScope. 241 // aTargetScope can be: 242 // - the system-principal scope when we want to dispatch the ConsoleEvent to 243 // nsIConsoleAPIStorage (See the comment in Console.cpp about the use of 244 // xpc::PrivilegedJunkScope() 245 // - the mConsoleEventNotifier->CallableGlobal() when we want to notify this 246 // handler about a new ConsoleEvent. 247 // - It can be the global from the JSContext when RetrieveConsoleEvents is 248 // called. 249 static bool PopulateConsoleNotificationInTheTargetScope( 250 JSContext* aCx, const Sequence<JS::Value>& aArguments, 251 JS::Handle<JSObject*> aTargetScope, 252 JS::MutableHandle<JS::Value> aEventValue, ConsoleCallData* aData, 253 nsTArray<nsString>* aGroupStack); 254 255 enum TimerStatus { 256 eTimerUnknown, 257 eTimerDone, 258 eTimerAlreadyExists, 259 eTimerDoesntExist, 260 eTimerJSException, 261 eTimerMaxReached, 262 }; 263 264 static JS::Value CreateTimerError(JSContext* aCx, const nsAString& aLabel, 265 TimerStatus aStatus); 266 267 // StartTimer is called on the owning thread and populates aTimerLabel and 268 // aTimerValue. 269 // * aCx - the JSContext rooting aName. 270 // * aName - this is (should be) the name of the timer as JS::Value. 271 // * aTimestamp - the monotonicTimer for this context taken from 272 // performance.now(). 273 // * aTimerLabel - This label will be populated with the aName converted to a 274 // string. 275 // * aTimerValue - the StartTimer value stored into (or taken from) 276 // mTimerRegistry. 277 TimerStatus StartTimer(JSContext* aCx, const JS::Value& aName, 278 DOMHighResTimeStamp aTimestamp, nsAString& aTimerLabel, 279 DOMHighResTimeStamp* aTimerValue); 280 281 // CreateStartTimerValue generates a ConsoleTimerStart dictionary exposed as 282 // JS::Value. If aTimerStatus is false, it generates a ConsoleTimerError 283 // instead. It's called only after the execution StartTimer on the owning 284 // thread. 285 // * aCx - this is the context that will root the returned value. 286 // * aTimerLabel - this label must be what StartTimer received as aTimerLabel. 287 // * aTimerStatus - the return value of StartTimer. 288 static JS::Value CreateStartTimerValue(JSContext* aCx, 289 const nsAString& aTimerLabel, 290 TimerStatus aTimerStatus); 291 292 // LogTimer follows the same pattern as StartTimer: it runs on the 293 // owning thread and populates aTimerLabel and aTimerDuration, used by 294 // CreateLogOrEndTimerValue. 295 // * aCx - the JSContext rooting aName. 296 // * aName - this is (should be) the name of the timer as JS::Value. 297 // * aTimestamp - the monotonicTimer for this context taken from 298 // performance.now(). 299 // * aTimerLabel - This label will be populated with the aName converted to a 300 // string. 301 // * aTimerDuration - the difference between aTimestamp and when the timer 302 // started (see StartTimer). 303 // * aCancelTimer - if true, the timer is removed from the table. 304 TimerStatus LogTimer(JSContext* aCx, const JS::Value& aName, 305 DOMHighResTimeStamp aTimestamp, nsAString& aTimerLabel, 306 double* aTimerDuration, bool aCancelTimer); 307 308 // This method generates a ConsoleTimerEnd dictionary exposed as JS::Value, or 309 // a ConsoleTimerError dictionary if aTimerStatus is false. See LogTimer. 310 // * aCx - this is the context that will root the returned value. 311 // * aTimerLabel - this label must be what LogTimer received as aTimerLabel. 312 // * aTimerDuration - this is what LogTimer received as aTimerDuration 313 // * aTimerStatus - the return value of LogTimer. 314 static JS::Value CreateLogOrEndTimerValue(JSContext* aCx, 315 const nsAString& aLabel, 316 double aDuration, 317 TimerStatus aStatus); 318 319 // The method populates a Sequence from an array of JS::Value. 320 bool ArgumentsToValueList(const Sequence<JS::Value>& aData, 321 Sequence<JS::Value>& aSequence) const; 322 323 // This method follows the same pattern as StartTimer: its runs on the owning 324 // thread and populate aCountLabel, used by CreateCounterOrResetCounterValue. 325 // Returns 3 possible values: 326 // * MAX_PAGE_COUNTERS in case of error that has to be reported; 327 // * 0 in case of a CX exception. The operation cannot continue; 328 // * the incremented counter value. 329 // Params: 330 // * aCx - the JSContext rooting aData. 331 // * aData - the arguments received by the console.count() method. 332 // * aCountLabel - the label that will be populated by this method. 333 uint32_t IncreaseCounter(JSContext* aCx, const Sequence<JS::Value>& aData, 334 nsAString& aCountLabel); 335 336 // This method follows the same pattern as StartTimer: its runs on the owning 337 // thread and populate aCountLabel, used by CreateCounterResetValue. Returns 338 // 3 possible values: 339 // * MAX_PAGE_COUNTERS in case of error that has to be reported; 340 // * 0 elsewhere. In case of a CX exception, aCountLabel will be an empty 341 // string. 342 // Params: 343 // * aCx - the JSContext rooting aData. 344 // * aData - the arguments received by the console.count() method. 345 // * aCountLabel - the label that will be populated by this method. 346 uint32_t ResetCounter(JSContext* aCx, const Sequence<JS::Value>& aData, 347 nsAString& aCountLabel); 348 349 static bool ShouldIncludeStackTrace(MethodName aMethodName); 350 351 void AssertIsOnOwningThread() const; 352 353 bool IsShuttingDown() const; 354 355 bool MonotonicTimer(JSContext* aCx, MethodName aMethodName, 356 const Sequence<JS::Value>& aData, 357 DOMHighResTimeStamp* aTimeStamp); 358 359 void StringifyElement(Element* aElement, nsAString& aOut); 360 361 MOZ_CAN_RUN_SCRIPT 362 void MaybeExecuteDumpFunction(JSContext* aCx, MethodName aMethodName, 363 const nsAString& aMethodString, 364 const Sequence<JS::Value>& aData, 365 nsIStackFrame* aStack, 366 DOMHighResTimeStamp aMonotonicTimer); 367 368 MOZ_CAN_RUN_SCRIPT 369 nsString GetDumpMessage(JSContext* aCx, MethodName aMethodName, 370 const nsAString& aMethodString, 371 const Sequence<JS::Value>& aData, 372 nsIStackFrame* aStack, 373 DOMHighResTimeStamp aMonotonicTimer, 374 bool aIsForMozLog); 375 376 MOZ_CAN_RUN_SCRIPT 377 void ExecuteDumpFunction(const nsAString& aMessage); 378 379 bool ShouldProceed(MethodName aName) const; 380 381 uint32_t WebIDLLogLevelToInteger(ConsoleLogLevel aLevel) const; 382 383 uint32_t InternalLogLevelToInteger(MethodName aName) const; 384 LogLevel InternalLogLevelToMozLog(MethodName aName) const; 385 386 class ArgumentData { 387 public: 388 bool Initialize(JSContext* aCx, const Sequence<JS::Value>& aArguments); 389 void Trace(const TraceCallbacks& aCallbacks, void* aClosure); 390 bool PopulateArgumentsSequence(Sequence<JS::Value>& aSequence) const; 391 JSObject* Global() const { return mGlobal; } 392 393 private: 394 void AssertIsOnOwningThread() const { 395 NS_ASSERT_OWNINGTHREAD(ArgumentData); 396 } 397 398 NS_DECL_OWNINGTHREAD; 399 JS::Heap<JSObject*> mGlobal; 400 nsTArray<JS::Heap<JS::Value>> mArguments; 401 }; 402 403 // Owning/CC thread only 404 nsCOMPtr<nsIGlobalObject> mGlobal; 405 406 // Touched on the owner thread. 407 nsTHashMap<nsStringHashKey, DOMHighResTimeStamp> mTimerRegistry; 408 nsTHashMap<nsStringHashKey, uint32_t> mCounterRegistry; 409 410 nsTArray<RefPtr<ConsoleCallData>> mCallDataStorage; 411 // These are references to the arguments we received in each call 412 // from the DOM bindings. 413 // Vector<T> supports non-memmovable types such as ArgumentData 414 // (without any need to jump through hoops like 415 // MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR_FOR_TEMPLATE for nsTArray). 416 Vector<ArgumentData> mArgumentStorage; 417 418 RefPtr<AnyCallback> mConsoleEventNotifier; 419 420 RefPtr<MainThreadConsoleData> mMainThreadData; 421 // This is the stack for grouping relating to Console-thread events, when 422 // the Console thread is not the main thread. 423 nsTArray<nsString> mGroupStack; 424 425 uint64_t mOuterID; 426 uint64_t mInnerID; 427 428 // Set only by ConsoleInstance: 429 nsString mConsoleID; 430 nsString mPassedInnerID; 431 RefPtr<ConsoleInstanceDumpCallback> mDumpFunction; 432 bool mDumpToStdout; 433 LogModule* mLogModule; 434 nsString mPrefix; 435 bool mChromeInstance; 436 uint32_t mCurrentLogLevel; 437 438 enum { eUnknown, eInitialized, eShuttingDown } mStatus; 439 440 // This is used when Console is created and it's used only for JSM custom 441 // console instance. 442 mozilla::TimeStamp mCreationTimeStamp; 443 444 friend class ConsoleCallData; 445 friend class ConsoleCallDataWorkletRunnable; 446 friend class ConsoleInstance; 447 friend class ConsoleProfileWorkerRunnable; 448 friend class ConsoleProfileWorkletRunnable; 449 friend class ConsoleRunnable; 450 friend class ConsoleWorkerRunnable; 451 friend class ConsoleWorkletRunnable; 452 friend class MainThreadConsoleData; 453 }; 454 455 } // namespace mozilla::dom 456 457 #endif /* mozilla_dom_Console_h */