summaryrefslogtreecommitdiffstats
path: root/dom/console/Console.h
blob: 5898c6b0f54ab6f4de4ae58d765f5dff322069d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
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/dom/ConsoleInstanceBinding.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;
  uint32_t mCurrentLogLevel;

  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 */