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/dom/ConsoleBinding.h" 12 #include "mozilla/TimeStamp.h" 13 #include "nsCycleCollectionParticipant.h" 14 #include "nsTHashMap.h" 15 #include "nsHashKeys.h" 16 #include "nsIObserver.h" 17 #include "nsWeakReference.h" 18 19 class nsIConsoleAPIStorage; 20 class nsIGlobalObject; 21 class nsPIDOMWindowInner; 22 class nsIStackFrame; 23 24 namespace mozilla { 25 namespace 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 static already_AddRefed<ConsoleInstance> CreateInstance( 138 const GlobalObject& aGlobal, const ConsoleInstanceOptions& aOptions); 139 140 void ClearStorage(); 141 142 void RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents, 143 ErrorResult& aRv); 144 145 void SetConsoleEventHandler(AnyCallback* aHandler); 146 147 private: 148 Console(JSContext* aCx, nsIGlobalObject* aGlobal, uint64_t aOuterWindowID, 149 uint64_t aInnerWIndowID); 150 ~Console(); 151 152 void Initialize(ErrorResult& aRv); 153 154 void Shutdown(); 155 156 enum MethodName { 157 MethodLog, 158 MethodInfo, 159 MethodWarn, 160 MethodError, 161 MethodException, 162 MethodDebug, 163 MethodTable, 164 MethodTrace, 165 MethodDir, 166 MethodDirxml, 167 MethodGroup, 168 MethodGroupCollapsed, 169 MethodGroupEnd, 170 MethodTime, 171 MethodTimeLog, 172 MethodTimeEnd, 173 MethodTimeStamp, 174 MethodAssert, 175 MethodCount, 176 MethodCountReset, 177 MethodClear, 178 MethodProfile, 179 MethodProfileEnd, 180 }; 181 182 static already_AddRefed<Console> GetConsole(const GlobalObject& aGlobal); 183 184 static already_AddRefed<Console> GetConsoleInternal( 185 const GlobalObject& aGlobal, ErrorResult& aRv); 186 187 MOZ_CAN_RUN_SCRIPT 188 static void ProfileMethod(const GlobalObject& aGlobal, MethodName aName, 189 const nsAString& aAction, 190 const Sequence<JS::Value>& aData); 191 192 MOZ_CAN_RUN_SCRIPT 193 void ProfileMethodInternal(JSContext* aCx, MethodName aName, 194 const nsAString& aAction, 195 const Sequence<JS::Value>& aData); 196 197 // Implementation of the mainthread-only parts of ProfileMethod. 198 // This is indepedent of console instance state. 199 static void ProfileMethodMainthread(JSContext* aCx, const nsAString& aAction, 200 const Sequence<JS::Value>& aData); 201 202 MOZ_CAN_RUN_SCRIPT 203 static void Method(const GlobalObject& aGlobal, MethodName aName, 204 const nsAString& aString, 205 const Sequence<JS::Value>& aData); 206 207 MOZ_CAN_RUN_SCRIPT 208 void MethodInternal(JSContext* aCx, MethodName aName, 209 const nsAString& aString, 210 const Sequence<JS::Value>& aData); 211 212 MOZ_CAN_RUN_SCRIPT 213 static void StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel, 214 const Sequence<JS::Value>& aData, 215 MethodName aMethodName, 216 const nsAString& aMethodString); 217 218 MOZ_CAN_RUN_SCRIPT 219 void StringMethodInternal(JSContext* aCx, const nsAString& aLabel, 220 const Sequence<JS::Value>& aData, 221 MethodName aMethodName, 222 const nsAString& aMethodString); 223 224 MainThreadConsoleData* GetOrCreateMainThreadData(); 225 226 // Returns true on success; otherwise false. 227 bool StoreCallData(JSContext* aCx, ConsoleCallData* aCallData, 228 const Sequence<JS::Value>& aArguments); 229 230 void UnstoreCallData(ConsoleCallData* aData); 231 232 // aCx and aArguments must be in the same JS compartment. 233 MOZ_CAN_RUN_SCRIPT 234 void NotifyHandler(JSContext* aCx, const Sequence<JS::Value>& aArguments, 235 ConsoleCallData* aData); 236 237 // PopulateConsoleNotificationInTheTargetScope receives aCx and aArguments in 238 // the same JS compartment and populates the ConsoleEvent object 239 // (aEventValue) in the aTargetScope. 240 // aTargetScope can be: 241 // - the system-principal scope when we want to dispatch the ConsoleEvent to 242 // nsIConsoleAPIStorage (See the comment in Console.cpp about the use of 243 // xpc::PrivilegedJunkScope() 244 // - the mConsoleEventNotifier->CallableGlobal() when we want to notify this 245 // handler about a new ConsoleEvent. 246 // - It can be the global from the JSContext when RetrieveConsoleEvents is 247 // called. 248 static bool PopulateConsoleNotificationInTheTargetScope( 249 JSContext* aCx, const Sequence<JS::Value>& aArguments, 250 JS::Handle<JSObject*> aTargetScope, 251 JS::MutableHandle<JS::Value> aEventValue, ConsoleCallData* aData, 252 nsTArray<nsString>* aGroupStack); 253 254 enum TimerStatus { 255 eTimerUnknown, 256 eTimerDone, 257 eTimerAlreadyExists, 258 eTimerDoesntExist, 259 eTimerJSException, 260 eTimerMaxReached, 261 }; 262 263 static JS::Value CreateTimerError(JSContext* aCx, const nsAString& aLabel, 264 TimerStatus aStatus); 265 266 // StartTimer is called on the owning thread and populates aTimerLabel and 267 // aTimerValue. 268 // * aCx - the JSContext rooting aName. 269 // * aName - this is (should be) the name of the timer as JS::Value. 270 // * aTimestamp - the monotonicTimer for this context taken from 271 // performance.now(). 272 // * aTimerLabel - This label will be populated with the aName converted to a 273 // string. 274 // * aTimerValue - the StartTimer value stored into (or taken from) 275 // mTimerRegistry. 276 TimerStatus StartTimer(JSContext* aCx, const JS::Value& aName, 277 DOMHighResTimeStamp aTimestamp, nsAString& aTimerLabel, 278 DOMHighResTimeStamp* aTimerValue); 279 280 // CreateStartTimerValue generates a ConsoleTimerStart dictionary exposed as 281 // JS::Value. If aTimerStatus is false, it generates a ConsoleTimerError 282 // instead. It's called only after the execution StartTimer on the owning 283 // thread. 284 // * aCx - this is the context that will root the returned value. 285 // * aTimerLabel - this label must be what StartTimer received as aTimerLabel. 286 // * aTimerStatus - the return value of StartTimer. 287 static JS::Value CreateStartTimerValue(JSContext* aCx, 288 const nsAString& aTimerLabel, 289 TimerStatus aTimerStatus); 290 291 // LogTimer follows the same pattern as StartTimer: it runs on the 292 // owning thread and populates aTimerLabel and aTimerDuration, used by 293 // CreateLogOrEndTimerValue. 294 // * aCx - the JSContext rooting aName. 295 // * aName - this is (should be) the name of the timer as JS::Value. 296 // * aTimestamp - the monotonicTimer for this context taken from 297 // performance.now(). 298 // * aTimerLabel - This label will be populated with the aName converted to a 299 // string. 300 // * aTimerDuration - the difference between aTimestamp and when the timer 301 // started (see StartTimer). 302 // * aCancelTimer - if true, the timer is removed from the table. 303 TimerStatus LogTimer(JSContext* aCx, const JS::Value& aName, 304 DOMHighResTimeStamp aTimestamp, nsAString& aTimerLabel, 305 double* aTimerDuration, bool aCancelTimer); 306 307 // This method generates a ConsoleTimerEnd dictionary exposed as JS::Value, or 308 // a ConsoleTimerError dictionary if aTimerStatus is false. See LogTimer. 309 // * aCx - this is the context that will root the returned value. 310 // * aTimerLabel - this label must be what LogTimer received as aTimerLabel. 311 // * aTimerDuration - this is what LogTimer received as aTimerDuration 312 // * aTimerStatus - the return value of LogTimer. 313 static JS::Value CreateLogOrEndTimerValue(JSContext* aCx, 314 const nsAString& aLabel, 315 double aDuration, 316 TimerStatus aStatus); 317 318 // The method populates a Sequence from an array of JS::Value. 319 bool ArgumentsToValueList(const Sequence<JS::Value>& aData, 320 Sequence<JS::Value>& aSequence) const; 321 322 // This method follows the same pattern as StartTimer: its runs on the owning 323 // thread and populate aCountLabel, used by CreateCounterOrResetCounterValue. 324 // Returns 3 possible values: 325 // * MAX_PAGE_COUNTERS in case of error that has to be reported; 326 // * 0 in case of a CX exception. The operation cannot continue; 327 // * the incremented counter value. 328 // Params: 329 // * aCx - the JSContext rooting aData. 330 // * aData - the arguments received by the console.count() method. 331 // * aCountLabel - the label that will be populated by this method. 332 uint32_t IncreaseCounter(JSContext* aCx, const Sequence<JS::Value>& aData, 333 nsAString& aCountLabel); 334 335 // This method follows the same pattern as StartTimer: its runs on the owning 336 // thread and populate aCountLabel, used by CreateCounterResetValue. Returns 337 // 3 possible values: 338 // * MAX_PAGE_COUNTERS in case of error that has to be reported; 339 // * 0 elsewhere. In case of a CX exception, aCountLabel will be an empty 340 // string. 341 // Params: 342 // * aCx - the JSContext rooting aData. 343 // * aData - the arguments received by the console.count() method. 344 // * aCountLabel - the label that will be populated by this method. 345 uint32_t ResetCounter(JSContext* aCx, const Sequence<JS::Value>& aData, 346 nsAString& aCountLabel); 347 348 static bool ShouldIncludeStackTrace(MethodName aMethodName); 349 350 void AssertIsOnOwningThread() const; 351 352 bool IsShuttingDown() const; 353 354 bool MonotonicTimer(JSContext* aCx, MethodName aMethodName, 355 const Sequence<JS::Value>& aData, 356 DOMHighResTimeStamp* aTimeStamp); 357 358 MOZ_CAN_RUN_SCRIPT 359 void MaybeExecuteDumpFunction(JSContext* aCx, const nsAString& aMethodName, 360 const Sequence<JS::Value>& aData, 361 nsIStackFrame* aStack); 362 363 MOZ_CAN_RUN_SCRIPT 364 void MaybeExecuteDumpFunctionForTime(JSContext* aCx, MethodName aMethodName, 365 const nsAString& aMethodString, 366 uint64_t aMonotonicTimer, 367 const JS::Value& aData); 368 369 MOZ_CAN_RUN_SCRIPT 370 void ExecuteDumpFunction(const nsAString& aMessage); 371 372 bool IsEnabled(JSContext* aCx) const; 373 374 bool ShouldProceed(MethodName aName) const; 375 376 uint32_t WebIDLLogLevelToInteger(ConsoleLogLevel aLevel) const; 377 378 uint32_t InternalLogLevelToInteger(MethodName aName) const; 379 380 class ArgumentData { 381 public: 382 bool Initialize(JSContext* aCx, const Sequence<JS::Value>& aArguments); 383 void Trace(const TraceCallbacks& aCallbacks, void* aClosure); 384 bool PopulateArgumentsSequence(Sequence<JS::Value>& aSequence) const; Global()385 JSObject* Global() const { return mGlobal; } 386 387 private: AssertIsOnOwningThread()388 void AssertIsOnOwningThread() const { 389 NS_ASSERT_OWNINGTHREAD(ArgumentData); 390 } 391 392 NS_DECL_OWNINGTHREAD; 393 JS::Heap<JSObject*> mGlobal; 394 nsTArray<JS::Heap<JS::Value>> mArguments; 395 }; 396 397 // Owning/CC thread only 398 nsCOMPtr<nsIGlobalObject> mGlobal; 399 400 // Touched on the owner thread. 401 nsTHashMap<nsStringHashKey, DOMHighResTimeStamp> mTimerRegistry; 402 nsTHashMap<nsStringHashKey, uint32_t> mCounterRegistry; 403 404 nsTArray<RefPtr<ConsoleCallData>> mCallDataStorage; 405 // These are references to the arguments we received in each call 406 // from the DOM bindings. 407 // Vector<T> supports non-memmovable types such as ArgumentData 408 // (without any need to jump through hoops like 409 // MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR_FOR_TEMPLATE for nsTArray). 410 Vector<ArgumentData> mArgumentStorage; 411 412 RefPtr<AnyCallback> mConsoleEventNotifier; 413 414 RefPtr<MainThreadConsoleData> mMainThreadData; 415 // This is the stack for grouping relating to Console-thread events, when 416 // the Console thread is not the main thread. 417 nsTArray<nsString> mGroupStack; 418 419 uint64_t mOuterID; 420 uint64_t mInnerID; 421 422 // Set only by ConsoleInstance: 423 nsString mConsoleID; 424 nsString mPassedInnerID; 425 RefPtr<ConsoleInstanceDumpCallback> mDumpFunction; 426 bool mDumpToStdout; 427 nsString mPrefix; 428 bool mChromeInstance; 429 ConsoleLogLevel mMaxLogLevel; 430 nsString mMaxLogLevelPref; 431 432 enum { eUnknown, eInitialized, eShuttingDown } mStatus; 433 434 // This is used when Console is created and it's used only for JSM custom 435 // console instance. 436 mozilla::TimeStamp mCreationTimeStamp; 437 438 friend class ConsoleCallData; 439 friend class ConsoleCallDataWorkletRunnable; 440 friend class ConsoleInstance; 441 friend class ConsoleProfileWorkerRunnable; 442 friend class ConsoleProfileWorkletRunnable; 443 friend class ConsoleRunnable; 444 friend class ConsoleWorkerRunnable; 445 friend class ConsoleWorkletRunnable; 446 friend class MainThreadConsoleData; 447 }; 448 449 } // namespace dom 450 } // namespace mozilla 451 452 #endif /* mozilla_dom_Console_h */ 453