Line data Source code
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 "mozilla/dom/BindingDeclarations.h"
11 : #include "mozilla/ErrorResult.h"
12 : #include "mozilla/JSObjectHolder.h"
13 : #include "nsCycleCollectionParticipant.h"
14 : #include "nsDataHashtable.h"
15 : #include "nsHashKeys.h"
16 : #include "nsIObserver.h"
17 : #include "nsWeakReference.h"
18 : #include "nsDOMNavigationTiming.h"
19 : #include "nsPIDOMWindow.h"
20 :
21 : class nsIConsoleAPIStorage;
22 : class nsIPrincipal;
23 :
24 : namespace mozilla {
25 : namespace dom {
26 :
27 : class AnyCallback;
28 : class ConsoleCallData;
29 : class ConsoleRunnable;
30 : class ConsoleCallDataRunnable;
31 : class ConsoleProfileRunnable;
32 : struct ConsoleTimerError;
33 : struct ConsoleStackEntry;
34 :
35 : class Console final : public nsIObserver
36 : , public nsSupportsWeakReference
37 : {
38 : public:
39 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
40 13 : NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Console, nsIObserver)
41 : NS_DECL_NSIOBSERVER
42 :
43 : static already_AddRefed<Console>
44 : Create(nsPIDOMWindowInner* aWindow, ErrorResult& aRv);
45 :
46 : // WebIDL methods
47 : nsPIDOMWindowInner* GetParentObject() const
48 : {
49 : return mWindow;
50 : }
51 :
52 : static void
53 : Log(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
54 :
55 : static void
56 : Info(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
57 :
58 : static void
59 : Warn(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
60 :
61 : static void
62 : Error(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
63 :
64 : static void
65 : Exception(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
66 :
67 : static void
68 : Debug(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
69 :
70 : static void
71 : Table(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
72 :
73 : static void
74 : Trace(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
75 :
76 : static void
77 : Dir(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
78 :
79 : static void
80 : Dirxml(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
81 :
82 : static void
83 : Group(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
84 :
85 : static void
86 : GroupCollapsed(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
87 :
88 : static void
89 : GroupEnd(const GlobalObject& aGlobal);
90 :
91 : static void
92 : Time(const GlobalObject& aGlobal, const nsAString& aLabel);
93 :
94 : static void
95 : TimeEnd(const GlobalObject& aGlobal, const nsAString& aLabel);
96 :
97 : static void
98 : TimeStamp(const GlobalObject& aGlobal, const JS::Handle<JS::Value> aData);
99 :
100 : static void
101 : Profile(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
102 :
103 : static void
104 : ProfileEnd(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData);
105 :
106 : static void
107 : Assert(const GlobalObject& aGlobal, bool aCondition,
108 : const Sequence<JS::Value>& aData);
109 :
110 : static void
111 : Count(const GlobalObject& aGlobal, const nsAString& aLabel);
112 :
113 : static void
114 : Clear(const GlobalObject& aGlobal);
115 :
116 : void
117 : ClearStorage();
118 :
119 : void
120 : RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents,
121 : ErrorResult& aRv);
122 :
123 : void
124 : SetConsoleEventHandler(AnyCallback* aHandler);
125 :
126 : private:
127 : explicit Console(nsPIDOMWindowInner* aWindow);
128 : ~Console();
129 :
130 : void
131 : Initialize(ErrorResult& aRv);
132 :
133 : void
134 : Shutdown();
135 :
136 : enum MethodName
137 : {
138 : MethodLog,
139 : MethodInfo,
140 : MethodWarn,
141 : MethodError,
142 : MethodException,
143 : MethodDebug,
144 : MethodTable,
145 : MethodTrace,
146 : MethodDir,
147 : MethodDirxml,
148 : MethodGroup,
149 : MethodGroupCollapsed,
150 : MethodGroupEnd,
151 : MethodTime,
152 : MethodTimeEnd,
153 : MethodTimeStamp,
154 : MethodAssert,
155 : MethodCount,
156 : MethodClear
157 : };
158 :
159 : static already_AddRefed<Console>
160 : GetConsole(const GlobalObject& aGlobal);
161 :
162 : static Console*
163 : GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult &aRv);
164 :
165 : static void
166 : ProfileMethod(const GlobalObject& aGlobal, const nsAString& aAction,
167 : const Sequence<JS::Value>& aData);
168 :
169 : void
170 : ProfileMethodInternal(JSContext* aCx, const nsAString& aAction,
171 : const Sequence<JS::Value>& aData);
172 :
173 : static void
174 : Method(const GlobalObject& aGlobal, MethodName aName,
175 : const nsAString& aString, const Sequence<JS::Value>& aData);
176 :
177 : void
178 : MethodInternal(JSContext* aCx, MethodName aName,
179 : const nsAString& aString, const Sequence<JS::Value>& aData);
180 :
181 : static void
182 : StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel,
183 : MethodName aMethodName, const nsAString& aMethodString);
184 :
185 : // This method must receive aCx and aArguments in the same JSCompartment.
186 : void
187 : ProcessCallData(JSContext* aCx,
188 : ConsoleCallData* aData,
189 : const Sequence<JS::Value>& aArguments);
190 :
191 : void
192 : StoreCallData(ConsoleCallData* aData);
193 :
194 : void
195 : UnstoreCallData(ConsoleCallData* aData);
196 :
197 : // Read in Console.cpp how this method is used.
198 : void
199 : ReleaseCallData(ConsoleCallData* aCallData);
200 :
201 : // aCx and aArguments must be in the same JS compartment.
202 : void
203 : NotifyHandler(JSContext* aCx,
204 : const Sequence<JS::Value>& aArguments,
205 : ConsoleCallData* aData);
206 :
207 : // PopulateConsoleNotificationInTheTargetScope receives aCx and aArguments in
208 : // the same JS compartment and populates the ConsoleEvent object (aValue) in
209 : // the aTargetScope.
210 : // aTargetScope can be:
211 : // - the system-principal scope when we want to dispatch the ConsoleEvent to
212 : // nsIConsoleAPIStorage (See the comment in Console.cpp about the use of
213 : // xpc::PrivilegedJunkScope()
214 : // - the mConsoleEventNotifier->Callable() scope when we want to notify this
215 : // handler about a new ConsoleEvent.
216 : // - It can be the global from the JSContext when RetrieveConsoleEvents is
217 : // called.
218 : bool
219 : PopulateConsoleNotificationInTheTargetScope(JSContext* aCx,
220 : const Sequence<JS::Value>& aArguments,
221 : JSObject* aTargetScope,
222 : JS::MutableHandle<JS::Value> aValue,
223 : ConsoleCallData* aData);
224 :
225 : // If the first JS::Value of the array is a string, this method uses it to
226 : // format a string. The supported sequences are:
227 : // %s - string
228 : // %d,%i - integer
229 : // %f - double
230 : // %o,%O - a JS object.
231 : // %c - style string.
232 : // The output is an array where any object is a separated item, the rest is
233 : // unified in a format string.
234 : // Example if the input is:
235 : // "string: %s, integer: %d, object: %o, double: %d", 's', 1, window, 0.9
236 : // The output will be:
237 : // [ "string: s, integer: 1, object: ", window, ", double: 0.9" ]
238 : //
239 : // The aStyles array is populated with the style strings that the function
240 : // finds based the format string. The index of the styles matches the indexes
241 : // of elements that need the custom styling from aSequence. For elements with
242 : // no custom styling the array is padded with null elements.
243 : bool
244 : ProcessArguments(JSContext* aCx, const Sequence<JS::Value>& aData,
245 : Sequence<JS::Value>& aSequence,
246 : Sequence<nsString>& aStyles) const;
247 :
248 : void
249 : MakeFormatString(nsCString& aFormat, int32_t aInteger, int32_t aMantissa,
250 : char aCh) const;
251 :
252 : // Stringify and Concat all the JS::Value in a single string using ' ' as
253 : // separator. The new group name will be stored in mGroupStack array.
254 : void
255 : ComposeAndStoreGroupName(JSContext* aCx, const Sequence<JS::Value>& aData,
256 : nsAString& aName);
257 :
258 : // Remove the last group name and return that name. It returns false if
259 : // mGroupStack is empty.
260 : bool
261 : UnstoreGroupName(nsAString& aName);
262 :
263 : enum TimerStatus {
264 : eTimerUnknown,
265 : eTimerDone,
266 : eTimerAlreadyExists,
267 : eTimerDoesntExist,
268 : eTimerJSException,
269 : eTimerMaxReached,
270 : };
271 :
272 : JS::Value
273 : CreateTimerError(JSContext* aCx, const nsAString& aTimerLabel,
274 : TimerStatus aStatus) const;
275 :
276 : // StartTimer is called on the owning thread and populates aTimerLabel and
277 : // aTimerValue.
278 : // * aCx - the JSContext rooting aName.
279 : // * aName - this is (should be) the name of the timer as JS::Value.
280 : // * aTimestamp - the monotonicTimer for this context taken from
281 : // performance.now().
282 : // * aTimerLabel - This label will be populated with the aName converted to a
283 : // string.
284 : // * aTimerValue - the StartTimer value stored into (or taken from)
285 : // mTimerRegistry.
286 : TimerStatus
287 : StartTimer(JSContext* aCx, const JS::Value& aName,
288 : DOMHighResTimeStamp aTimestamp,
289 : nsAString& aTimerLabel,
290 : DOMHighResTimeStamp* aTimerValue);
291 :
292 : // CreateStartTimerValue generates a ConsoleTimerStart dictionary exposed as
293 : // JS::Value. If aTimerStatus is false, it generates a ConsoleTimerError
294 : // instead. It's called only after the execution StartTimer on the owning
295 : // thread.
296 : // * aCx - this is the context that will root the returned value.
297 : // * aTimerLabel - this label must be what StartTimer received as aTimerLabel.
298 : // * aTimerStatus - the return value of StartTimer.
299 : JS::Value
300 : CreateStartTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
301 : TimerStatus aTimerStatus) const;
302 :
303 : // StopTimer follows the same pattern as StartTimer: it runs on the
304 : // owning thread and populates aTimerLabel and aTimerDuration, used by
305 : // CreateStopTimerValue.
306 : // * aCx - the JSContext rooting aName.
307 : // * aName - this is (should be) the name of the timer as JS::Value.
308 : // * aTimestamp - the monotonicTimer for this context taken from
309 : // performance.now().
310 : // * aTimerLabel - This label will be populated with the aName converted to a
311 : // string.
312 : // * aTimerDuration - the difference between aTimestamp and when the timer
313 : // started (see StartTimer).
314 : TimerStatus
315 : StopTimer(JSContext* aCx, const JS::Value& aName,
316 : DOMHighResTimeStamp aTimestamp,
317 : nsAString& aTimerLabel,
318 : double* aTimerDuration);
319 :
320 : // This method generates a ConsoleTimerEnd dictionary exposed as JS::Value, or
321 : // a ConsoleTimerError dictionary if aTimerStatus is false. See StopTimer.
322 : // * aCx - this is the context that will root the returned value.
323 : // * aTimerLabel - this label must be what StopTimer received as aTimerLabel.
324 : // * aTimerDuration - this is what StopTimer received as aTimerDuration
325 : // * aTimerStatus - the return value of StopTimer.
326 : JS::Value
327 : CreateStopTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
328 : double aTimerDuration,
329 : TimerStatus aTimerStatus) const;
330 :
331 : // The method populates a Sequence from an array of JS::Value.
332 : bool
333 : ArgumentsToValueList(const Sequence<JS::Value>& aData,
334 : Sequence<JS::Value>& aSequence) const;
335 :
336 : // This method follows the same pattern as StartTimer: its runs on the owning
337 : // thread and populate aCountLabel, used by CreateCounterValue. Returns
338 : // 3 possible values:
339 : // * MAX_PAGE_COUNTERS in case of error that has to be reported;
340 : // * 0 in case of a CX exception. The operation cannot continue;
341 : // * the incremented counter value.
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
347 : IncreaseCounter(JSContext* aCx, const Sequence<JS::Value>& aData,
348 : nsAString& aCountLabel);
349 :
350 : // This method generates a ConsoleCounter dictionary as JS::Value. If
351 : // aCountValue is == MAX_PAGE_COUNTERS it generates a ConsoleCounterError
352 : // instead. See IncreaseCounter.
353 : // * aCx - this is the context that will root the returned value.
354 : // * aCountLabel - this label must be what IncreaseCounter received as
355 : // aTimerLabel.
356 : // * aCountValue - the return value of IncreaseCounter.
357 : JS::Value
358 : CreateCounterValue(JSContext* aCx, const nsAString& aCountLabel,
359 : uint32_t aCountValue) const;
360 :
361 : bool
362 : ShouldIncludeStackTrace(MethodName aMethodName) const;
363 :
364 : JSObject*
365 : GetOrCreateSandbox(JSContext* aCx, nsIPrincipal* aPrincipal);
366 :
367 : void
368 : AssertIsOnOwningThread() const;
369 :
370 : bool
371 : IsShuttingDown() const;
372 :
373 : // All these nsCOMPtr are touched on main thread only.
374 : nsCOMPtr<nsPIDOMWindowInner> mWindow;
375 : nsCOMPtr<nsIConsoleAPIStorage> mStorage;
376 : RefPtr<JSObjectHolder> mSandbox;
377 :
378 : // Touched on the owner thread.
379 : nsDataHashtable<nsStringHashKey, DOMHighResTimeStamp> mTimerRegistry;
380 : nsDataHashtable<nsStringHashKey, uint32_t> mCounterRegistry;
381 :
382 : nsTArray<RefPtr<ConsoleCallData>> mCallDataStorage;
383 :
384 : // This array is used in a particular corner-case where:
385 : // 1. we are in a worker thread
386 : // 2. we have more than STORAGE_MAX_EVENTS
387 : // 3. but the main-thread ConsoleCallDataRunnable of the first one is still
388 : // running (this means that something very bad is happening on the
389 : // main-thread).
390 : // When this happens we want to keep the ConsoleCallData alive for traceing
391 : // its JSValues also if 'officially' this ConsoleCallData must be removed from
392 : // the storage.
393 : nsTArray<RefPtr<ConsoleCallData>> mCallDataStoragePending;
394 :
395 : RefPtr<AnyCallback> mConsoleEventNotifier;
396 :
397 : // This is the stack for groupping.
398 : nsTArray<nsString> mGroupStack;
399 :
400 : uint64_t mOuterID;
401 : uint64_t mInnerID;
402 :
403 : enum {
404 : eUnknown,
405 : eInitialized,
406 : eShuttingDown
407 : } mStatus;
408 :
409 : friend class ConsoleCallData;
410 : friend class ConsoleRunnable;
411 : friend class ConsoleCallDataRunnable;
412 : friend class ConsoleProfileRunnable;
413 : };
414 :
415 : } // namespace dom
416 : } // namespace mozilla
417 :
418 : #endif /* mozilla_dom_Console_h */
|