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