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 : #include "mozilla/dom/Console.h"
8 : #include "mozilla/dom/ConsoleBinding.h"
9 :
10 : #include "mozilla/dom/BlobBinding.h"
11 : #include "mozilla/dom/Exceptions.h"
12 : #include "mozilla/dom/File.h"
13 : #include "mozilla/dom/FunctionBinding.h"
14 : #include "mozilla/dom/Performance.h"
15 : #include "mozilla/dom/ScriptSettings.h"
16 : #include "mozilla/dom/StructuredCloneHolder.h"
17 : #include "mozilla/dom/ToJSValue.h"
18 : #include "mozilla/dom/WorkletGlobalScope.h"
19 : #include "mozilla/Maybe.h"
20 : #include "nsCycleCollectionParticipant.h"
21 : #include "nsDocument.h"
22 : #include "nsDOMNavigationTiming.h"
23 : #include "nsGlobalWindow.h"
24 : #include "nsJSUtils.h"
25 : #include "nsNetUtil.h"
26 : #include "WorkerPrivate.h"
27 : #include "WorkerRunnable.h"
28 : #include "WorkerScope.h"
29 : #include "xpcpublic.h"
30 : #include "nsContentUtils.h"
31 : #include "nsDocShell.h"
32 : #include "nsProxyRelease.h"
33 : #include "mozilla/ConsoleTimelineMarker.h"
34 : #include "mozilla/TimestampTimelineMarker.h"
35 :
36 : #include "nsIConsoleAPIStorage.h"
37 : #include "nsIDOMWindowUtils.h"
38 : #include "nsIInterfaceRequestorUtils.h"
39 : #include "nsILoadContext.h"
40 : #include "nsIProgrammingLanguage.h"
41 : #include "nsISensitiveInfoHiddenURI.h"
42 : #include "nsIServiceManager.h"
43 : #include "nsISupportsPrimitives.h"
44 : #include "nsIWebNavigation.h"
45 : #include "nsIXPConnect.h"
46 :
47 : // The maximum allowed number of concurrent timers per page.
48 : #define MAX_PAGE_TIMERS 10000
49 :
50 : // The maximum allowed number of concurrent counters per page.
51 : #define MAX_PAGE_COUNTERS 10000
52 :
53 : // The maximum stacktrace depth when populating the stacktrace array used for
54 : // console.trace().
55 : #define DEFAULT_MAX_STACKTRACE_DEPTH 200
56 :
57 : // This tags are used in the Structured Clone Algorithm to move js values from
58 : // worker thread to main thread
59 : #define CONSOLE_TAG_BLOB JS_SCTAG_USER_MIN
60 :
61 : // This value is taken from ConsoleAPIStorage.js
62 : #define STORAGE_MAX_EVENTS 1000
63 :
64 : using namespace mozilla::dom::exceptions;
65 : using namespace mozilla::dom::workers;
66 :
67 : namespace mozilla {
68 : namespace dom {
69 :
70 : struct
71 0 : ConsoleStructuredCloneData
72 : {
73 : nsCOMPtr<nsISupports> mParent;
74 : nsTArray<RefPtr<BlobImpl>> mBlobs;
75 : };
76 :
77 : /**
78 : * Console API in workers uses the Structured Clone Algorithm to move any value
79 : * from the worker thread to the main-thread. Some object cannot be moved and,
80 : * in these cases, we convert them to strings.
81 : * It's not the best, but at least we are able to show something.
82 : */
83 :
84 : class ConsoleCallData final
85 : {
86 : public:
87 5 : NS_INLINE_DECL_REFCOUNTING(ConsoleCallData)
88 :
89 1 : ConsoleCallData()
90 1 : : mMethodName(Console::MethodLog)
91 1 : , mTimeStamp(JS_Now() / PR_USEC_PER_MSEC)
92 : , mStartTimerValue(0)
93 : , mStartTimerStatus(Console::eTimerUnknown)
94 : , mStopTimerDuration(0)
95 : , mStopTimerStatus(Console::eTimerUnknown)
96 : , mCountValue(MAX_PAGE_COUNTERS)
97 : , mIDType(eUnknown)
98 : , mOuterIDNumber(0)
99 : , mInnerIDNumber(0)
100 2 : , mStatus(eUnused)
101 1 : {}
102 :
103 : bool
104 1 : Initialize(JSContext* aCx, Console::MethodName aName,
105 : const nsAString& aString,
106 : const Sequence<JS::Value>& aArguments,
107 : Console* aConsole)
108 : {
109 1 : AssertIsOnOwningThread();
110 1 : MOZ_ASSERT(aConsole);
111 :
112 : // We must be registered before doing any JS operation otherwise it can
113 : // happen that mCopiedArguments are not correctly traced.
114 1 : aConsole->StoreCallData(this);
115 :
116 1 : mMethodName = aName;
117 1 : mMethodString = aString;
118 :
119 1 : mGlobal = JS::CurrentGlobalOrNull(aCx);
120 :
121 2 : for (uint32_t i = 0; i < aArguments.Length(); ++i) {
122 1 : if (NS_WARN_IF(!mCopiedArguments.AppendElement(aArguments[i]))) {
123 0 : aConsole->UnstoreCallData(this);
124 0 : return false;
125 : }
126 : }
127 :
128 1 : return true;
129 : }
130 :
131 : void
132 1 : SetIDs(uint64_t aOuterID, uint64_t aInnerID)
133 : {
134 1 : MOZ_ASSERT(mIDType == eUnknown);
135 :
136 1 : mOuterIDNumber = aOuterID;
137 1 : mInnerIDNumber = aInnerID;
138 1 : mIDType = eNumber;
139 1 : }
140 :
141 : void
142 0 : SetIDs(const nsAString& aOuterID, const nsAString& aInnerID)
143 : {
144 0 : MOZ_ASSERT(mIDType == eUnknown);
145 :
146 0 : mOuterIDString = aOuterID;
147 0 : mInnerIDString = aInnerID;
148 0 : mIDType = eString;
149 0 : }
150 :
151 : void
152 1 : SetOriginAttributes(const OriginAttributes& aOriginAttributes)
153 : {
154 1 : mOriginAttributes = aOriginAttributes;
155 1 : }
156 :
157 : void
158 1 : SetAddonId(nsIPrincipal* aPrincipal)
159 : {
160 2 : nsAutoString addonId;
161 1 : aPrincipal->GetAddonId(addonId);
162 :
163 1 : mAddonId = addonId;
164 1 : }
165 :
166 : bool
167 0 : PopulateArgumentsSequence(Sequence<JS::Value>& aSequence) const
168 : {
169 0 : AssertIsOnOwningThread();
170 :
171 0 : for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
172 0 : if (NS_WARN_IF(!aSequence.AppendElement(mCopiedArguments[i],
173 : fallible))) {
174 0 : return false;
175 : }
176 : }
177 :
178 0 : return true;
179 : }
180 :
181 : void
182 0 : Trace(const TraceCallbacks& aCallbacks, void* aClosure)
183 : {
184 0 : ConsoleCallData* tmp = this;
185 0 : for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
186 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCopiedArguments[i])
187 : }
188 :
189 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal);
190 0 : }
191 :
192 : void
193 2 : AssertIsOnOwningThread() const
194 : {
195 2 : NS_ASSERT_OWNINGTHREAD(ConsoleCallData);
196 2 : }
197 :
198 : JS::Heap<JSObject*> mGlobal;
199 :
200 : // This is a copy of the arguments we received from the DOM bindings. Console
201 : // object traces them because this ConsoleCallData calls
202 : // RegisterConsoleCallData() in the Initialize().
203 : nsTArray<JS::Heap<JS::Value>> mCopiedArguments;
204 :
205 : Console::MethodName mMethodName;
206 : int64_t mTimeStamp;
207 :
208 : // These values are set in the owning thread and they contain the timestamp of
209 : // when the new timer has started, the name of it and the status of the
210 : // creation of it. If status is false, something went wrong. User
211 : // DOMHighResTimeStamp instead mozilla::TimeStamp because we use
212 : // monotonicTimer from Performance.now();
213 : // They will be set on the owning thread and never touched again on that
214 : // thread. They will be used in order to create a ConsoleTimerStart dictionary
215 : // when console.time() is used.
216 : DOMHighResTimeStamp mStartTimerValue;
217 : nsString mStartTimerLabel;
218 : Console::TimerStatus mStartTimerStatus;
219 :
220 : // These values are set in the owning thread and they contain the duration,
221 : // the name and the status of the StopTimer method. If status is false,
222 : // something went wrong. They will be set on the owning thread and never
223 : // touched again on that thread. They will be used in order to create a
224 : // ConsoleTimerEnd dictionary. This members are set when
225 : // console.timeEnd() is called.
226 : double mStopTimerDuration;
227 : nsString mStopTimerLabel;
228 : Console::TimerStatus mStopTimerStatus;
229 :
230 : // These 2 values are set by IncreaseCounter on the owning thread and they are
231 : // used CreateCounterValue. These members are set when console.count() is
232 : // called.
233 : nsString mCountLabel;
234 : uint32_t mCountValue;
235 :
236 : // The concept of outerID and innerID is misleading because when a
237 : // ConsoleCallData is created from a window, these are the window IDs, but
238 : // when the object is created from a SharedWorker, a ServiceWorker or a
239 : // subworker of a ChromeWorker these IDs are the type of worker and the
240 : // filename of the callee.
241 : // In Console.jsm the ID is 'jsm'.
242 : enum {
243 : eString,
244 : eNumber,
245 : eUnknown
246 : } mIDType;
247 :
248 : uint64_t mOuterIDNumber;
249 : nsString mOuterIDString;
250 :
251 : uint64_t mInnerIDNumber;
252 : nsString mInnerIDString;
253 :
254 : OriginAttributes mOriginAttributes;
255 :
256 : nsString mAddonId;
257 :
258 : nsString mMethodString;
259 :
260 : // Stack management is complicated, because we want to do it as
261 : // lazily as possible. Therefore, we have the following behavior:
262 : // 1) mTopStackFrame is initialized whenever we have any JS on the stack
263 : // 2) mReifiedStack is initialized if we're created in a worker.
264 : // 3) mStack is set (possibly to null if there is no JS on the stack) if
265 : // we're created on main thread.
266 : Maybe<ConsoleStackEntry> mTopStackFrame;
267 : Maybe<nsTArray<ConsoleStackEntry>> mReifiedStack;
268 : nsCOMPtr<nsIStackFrame> mStack;
269 :
270 : // mStatus is about the lifetime of this object. Console must take care of
271 : // keep it alive or not following this enumeration.
272 : enum {
273 : // If the object is created but it is owned by some runnable, this is its
274 : // status. It can be deleted at any time.
275 : eUnused,
276 :
277 : // When a runnable takes ownership of a ConsoleCallData and send it to
278 : // different thread, this is its status. Console cannot delete it at this
279 : // time.
280 : eInUse,
281 :
282 : // When a runnable owns this ConsoleCallData, we can't delete it directly.
283 : // instead, we mark it with this new status and we move it in
284 : // mCallDataStoragePending list in order to keep it alive an trace it
285 : // correctly. Once the runnable finishs its task, it will delete this object
286 : // calling ReleaseCallData().
287 : eToBeDeleted
288 : } mStatus;
289 :
290 : private:
291 1 : ~ConsoleCallData()
292 1 : {
293 1 : AssertIsOnOwningThread();
294 1 : MOZ_ASSERT(mStatus != eInUse);
295 1 : }
296 : };
297 :
298 : // This class is used to clear any exception at the end of this method.
299 : class ClearException
300 : {
301 : public:
302 0 : explicit ClearException(JSContext* aCx)
303 0 : : mCx(aCx)
304 : {
305 0 : }
306 :
307 0 : ~ClearException()
308 0 : {
309 0 : JS_ClearPendingException(mCx);
310 0 : }
311 :
312 : private:
313 : JSContext* mCx;
314 : };
315 :
316 : class ConsoleRunnable : public WorkerProxyToMainThreadRunnable
317 : , public StructuredCloneHolderBase
318 : {
319 : public:
320 0 : explicit ConsoleRunnable(Console* aConsole)
321 0 : : WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
322 0 : , mConsole(aConsole)
323 0 : {}
324 :
325 : virtual
326 0 : ~ConsoleRunnable()
327 0 : {
328 : // Clear the StructuredCloneHolderBase class.
329 0 : Clear();
330 0 : }
331 :
332 : bool
333 0 : Dispatch(JSContext* aCx)
334 : {
335 0 : mWorkerPrivate->AssertIsOnWorkerThread();
336 :
337 0 : if (NS_WARN_IF(!PreDispatch(aCx))) {
338 0 : RunBackOnWorkerThreadForCleanup();
339 0 : return false;
340 : }
341 :
342 0 : if (NS_WARN_IF(!WorkerProxyToMainThreadRunnable::Dispatch())) {
343 : // RunBackOnWorkerThreadForCleanup() will be called by
344 : // WorkerProxyToMainThreadRunnable::Dispatch().
345 0 : return false;
346 : }
347 :
348 0 : return true;
349 : }
350 :
351 : protected:
352 : void
353 0 : RunOnMainThread() override
354 : {
355 0 : AssertIsOnMainThread();
356 :
357 : // Walk up to our containing page
358 0 : WorkerPrivate* wp = mWorkerPrivate;
359 0 : while (wp->GetParent()) {
360 0 : wp = wp->GetParent();
361 : }
362 :
363 0 : nsPIDOMWindowInner* window = wp->GetWindow();
364 0 : if (!window) {
365 0 : RunWindowless();
366 : } else {
367 0 : RunWithWindow(window);
368 : }
369 0 : }
370 :
371 : void
372 0 : RunWithWindow(nsPIDOMWindowInner* aWindow)
373 : {
374 0 : AssertIsOnMainThread();
375 :
376 0 : AutoJSAPI jsapi;
377 0 : MOZ_ASSERT(aWindow);
378 :
379 0 : RefPtr<nsGlobalWindow> win = nsGlobalWindow::Cast(aWindow);
380 0 : if (NS_WARN_IF(!jsapi.Init(win))) {
381 0 : return;
382 : }
383 :
384 0 : MOZ_ASSERT(aWindow->IsInnerWindow());
385 0 : nsPIDOMWindowOuter* outerWindow = aWindow->GetOuterWindow();
386 0 : if (NS_WARN_IF(!outerWindow)) {
387 0 : return;
388 : }
389 :
390 0 : RunConsole(jsapi.cx(), outerWindow, aWindow);
391 : }
392 :
393 : void
394 0 : RunWindowless()
395 : {
396 0 : AssertIsOnMainThread();
397 :
398 0 : WorkerPrivate* wp = mWorkerPrivate;
399 0 : while (wp->GetParent()) {
400 0 : wp = wp->GetParent();
401 : }
402 :
403 0 : MOZ_ASSERT(!wp->GetWindow());
404 :
405 0 : AutoSafeJSContext cx;
406 :
407 0 : JS::Rooted<JSObject*> global(cx, mConsole->GetOrCreateSandbox(cx, wp->GetPrincipal()));
408 0 : if (NS_WARN_IF(!global)) {
409 0 : return;
410 : }
411 :
412 : // The CreateSandbox call returns a proxy to the actual sandbox object. We
413 : // don't need a proxy here.
414 0 : global = js::UncheckedUnwrap(global);
415 :
416 0 : JSAutoCompartment ac(cx, global);
417 :
418 0 : RunConsole(cx, nullptr, nullptr);
419 : }
420 :
421 : void
422 0 : RunBackOnWorkerThreadForCleanup() override
423 : {
424 0 : mWorkerPrivate->AssertIsOnWorkerThread();
425 0 : ReleaseData();
426 0 : mConsole = nullptr;
427 0 : }
428 :
429 : // This method is called in the owning thread of the Console object.
430 : virtual bool
431 : PreDispatch(JSContext* aCx) = 0;
432 :
433 : // This method is called in the main-thread.
434 : virtual void
435 : RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
436 : nsPIDOMWindowInner* aInnerWindow) = 0;
437 :
438 : // This method is called in the owning thread of the Console object.
439 : virtual void
440 : ReleaseData() = 0;
441 :
442 0 : virtual JSObject* CustomReadHandler(JSContext* aCx,
443 : JSStructuredCloneReader* aReader,
444 : uint32_t aTag,
445 : uint32_t aIndex) override
446 : {
447 0 : AssertIsOnMainThread();
448 :
449 0 : if (aTag == CONSOLE_TAG_BLOB) {
450 0 : MOZ_ASSERT(mClonedData.mBlobs.Length() > aIndex);
451 :
452 0 : JS::Rooted<JS::Value> val(aCx);
453 : {
454 : RefPtr<Blob> blob =
455 0 : Blob::Create(mClonedData.mParent, mClonedData.mBlobs.ElementAt(aIndex));
456 0 : if (!ToJSValue(aCx, blob, &val)) {
457 0 : return nullptr;
458 : }
459 : }
460 :
461 0 : return &val.toObject();
462 : }
463 :
464 0 : MOZ_CRASH("No other tags are supported.");
465 : return nullptr;
466 : }
467 :
468 0 : virtual bool CustomWriteHandler(JSContext* aCx,
469 : JSStructuredCloneWriter* aWriter,
470 : JS::Handle<JSObject*> aObj) override
471 : {
472 0 : RefPtr<Blob> blob;
473 0 : if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
474 0 : blob->Impl()->MayBeClonedToOtherThreads()) {
475 0 : if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB,
476 : mClonedData.mBlobs.Length()))) {
477 0 : return false;
478 : }
479 :
480 0 : mClonedData.mBlobs.AppendElement(blob->Impl());
481 0 : return true;
482 : }
483 :
484 0 : if (!JS_ObjectNotWritten(aWriter, aObj)) {
485 0 : return false;
486 : }
487 :
488 0 : JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
489 0 : JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
490 0 : if (NS_WARN_IF(!jsString)) {
491 0 : return false;
492 : }
493 :
494 0 : if (NS_WARN_IF(!JS_WriteString(aWriter, jsString))) {
495 0 : return false;
496 : }
497 :
498 0 : return true;
499 : }
500 :
501 : // This must be released on the worker thread.
502 : RefPtr<Console> mConsole;
503 :
504 : ConsoleStructuredCloneData mClonedData;
505 : };
506 :
507 : // This runnable appends a CallData object into the Console queue running on
508 : // the main-thread.
509 : class ConsoleCallDataRunnable final : public ConsoleRunnable
510 : {
511 : public:
512 0 : ConsoleCallDataRunnable(Console* aConsole,
513 : ConsoleCallData* aCallData)
514 0 : : ConsoleRunnable(aConsole)
515 0 : , mCallData(aCallData)
516 : {
517 0 : MOZ_ASSERT(aCallData);
518 0 : mWorkerPrivate->AssertIsOnWorkerThread();
519 0 : mCallData->AssertIsOnOwningThread();
520 0 : }
521 :
522 : private:
523 0 : ~ConsoleCallDataRunnable()
524 0 : {
525 0 : MOZ_ASSERT(!mCallData);
526 0 : }
527 :
528 : bool
529 0 : PreDispatch(JSContext* aCx) override
530 : {
531 0 : mWorkerPrivate->AssertIsOnWorkerThread();
532 0 : mCallData->AssertIsOnOwningThread();
533 :
534 0 : ClearException ce(aCx);
535 :
536 : JS::Rooted<JSObject*> arguments(aCx,
537 0 : JS_NewArrayObject(aCx, mCallData->mCopiedArguments.Length()));
538 0 : if (NS_WARN_IF(!arguments)) {
539 0 : return false;
540 : }
541 :
542 0 : JS::Rooted<JS::Value> arg(aCx);
543 0 : for (uint32_t i = 0; i < mCallData->mCopiedArguments.Length(); ++i) {
544 0 : arg = mCallData->mCopiedArguments[i];
545 0 : if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
546 : JSPROP_ENUMERATE))) {
547 0 : return false;
548 : }
549 : }
550 :
551 0 : JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
552 :
553 0 : if (NS_WARN_IF(!Write(aCx, value))) {
554 0 : return false;
555 : }
556 :
557 0 : mCallData->mStatus = ConsoleCallData::eInUse;
558 0 : return true;
559 : }
560 :
561 : void
562 0 : RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
563 : nsPIDOMWindowInner* aInnerWindow) override
564 : {
565 0 : AssertIsOnMainThread();
566 :
567 : // The windows have to run in parallel.
568 0 : MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
569 :
570 0 : if (aOuterWindow) {
571 0 : mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
572 : } else {
573 0 : ConsoleStackEntry frame;
574 0 : if (mCallData->mTopStackFrame) {
575 0 : frame = *mCallData->mTopStackFrame;
576 : }
577 :
578 0 : nsString id = frame.mFilename;
579 0 : nsString innerID;
580 0 : if (mWorkerPrivate->IsSharedWorker()) {
581 0 : innerID = NS_LITERAL_STRING("SharedWorker");
582 0 : } else if (mWorkerPrivate->IsServiceWorker()) {
583 0 : innerID = NS_LITERAL_STRING("ServiceWorker");
584 : // Use scope as ID so the webconsole can decide if the message should
585 : // show up per tab
586 0 : id.AssignWithConversion(mWorkerPrivate->ServiceWorkerScope());
587 : } else {
588 0 : innerID = NS_LITERAL_STRING("Worker");
589 : }
590 :
591 0 : mCallData->SetIDs(id, innerID);
592 : }
593 :
594 : // Now we could have the correct window (if we are not window-less).
595 0 : mClonedData.mParent = aInnerWindow;
596 :
597 0 : ProcessCallData(aCx);
598 :
599 0 : mClonedData.mParent = nullptr;
600 0 : }
601 :
602 : virtual void
603 0 : ReleaseData() override
604 : {
605 0 : mConsole->AssertIsOnOwningThread();
606 :
607 0 : if (mCallData->mStatus == ConsoleCallData::eToBeDeleted) {
608 0 : mConsole->ReleaseCallData(mCallData);
609 : } else {
610 0 : MOZ_ASSERT(mCallData->mStatus == ConsoleCallData::eInUse);
611 0 : mCallData->mStatus = ConsoleCallData::eUnused;
612 : }
613 :
614 0 : mCallData = nullptr;
615 0 : }
616 :
617 : void
618 0 : ProcessCallData(JSContext* aCx)
619 : {
620 0 : AssertIsOnMainThread();
621 :
622 0 : ClearException ce(aCx);
623 :
624 0 : JS::Rooted<JS::Value> argumentsValue(aCx);
625 0 : if (!Read(aCx, &argumentsValue)) {
626 0 : return;
627 : }
628 :
629 0 : MOZ_ASSERT(argumentsValue.isObject());
630 :
631 0 : JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
632 :
633 : uint32_t length;
634 0 : if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
635 0 : return;
636 : }
637 :
638 0 : Sequence<JS::Value> values;
639 0 : SequenceRooter<JS::Value> arguments(aCx, &values);
640 :
641 0 : for (uint32_t i = 0; i < length; ++i) {
642 0 : JS::Rooted<JS::Value> value(aCx);
643 :
644 0 : if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
645 0 : return;
646 : }
647 :
648 0 : if (!values.AppendElement(value, fallible)) {
649 0 : return;
650 : }
651 : }
652 :
653 0 : MOZ_ASSERT(values.Length() == length);
654 :
655 0 : mConsole->ProcessCallData(aCx, mCallData, values);
656 : }
657 :
658 : RefPtr<ConsoleCallData> mCallData;
659 : };
660 :
661 : // This runnable calls ProfileMethod() on the console on the main-thread.
662 0 : class ConsoleProfileRunnable final : public ConsoleRunnable
663 : {
664 : public:
665 0 : ConsoleProfileRunnable(Console* aConsole, const nsAString& aAction,
666 : const Sequence<JS::Value>& aArguments)
667 0 : : ConsoleRunnable(aConsole)
668 : , mAction(aAction)
669 0 : , mArguments(aArguments)
670 : {
671 0 : MOZ_ASSERT(aConsole);
672 0 : }
673 :
674 : private:
675 : bool
676 0 : PreDispatch(JSContext* aCx) override
677 : {
678 0 : ClearException ce(aCx);
679 :
680 : JS::Rooted<JSObject*> arguments(aCx,
681 0 : JS_NewArrayObject(aCx, mArguments.Length()));
682 0 : if (NS_WARN_IF(!arguments)) {
683 0 : return false;
684 : }
685 :
686 0 : JS::Rooted<JS::Value> arg(aCx);
687 0 : for (uint32_t i = 0; i < mArguments.Length(); ++i) {
688 0 : arg = mArguments[i];
689 0 : if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
690 : JSPROP_ENUMERATE))) {
691 0 : return false;
692 : }
693 : }
694 :
695 0 : JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
696 :
697 0 : if (NS_WARN_IF(!Write(aCx, value))) {
698 0 : return false;
699 : }
700 :
701 0 : return true;
702 : }
703 :
704 : void
705 0 : RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
706 : nsPIDOMWindowInner* aInnerWindow) override
707 : {
708 0 : AssertIsOnMainThread();
709 :
710 0 : ClearException ce(aCx);
711 :
712 : // Now we could have the correct window (if we are not window-less).
713 0 : mClonedData.mParent = aInnerWindow;
714 :
715 0 : JS::Rooted<JS::Value> argumentsValue(aCx);
716 0 : bool ok = Read(aCx, &argumentsValue);
717 0 : mClonedData.mParent = nullptr;
718 :
719 0 : if (!ok) {
720 0 : return;
721 : }
722 :
723 0 : MOZ_ASSERT(argumentsValue.isObject());
724 0 : JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
725 0 : if (NS_WARN_IF(!argumentsObj)) {
726 0 : return;
727 : }
728 :
729 : uint32_t length;
730 0 : if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
731 0 : return;
732 : }
733 :
734 0 : Sequence<JS::Value> arguments;
735 :
736 0 : for (uint32_t i = 0; i < length; ++i) {
737 0 : JS::Rooted<JS::Value> value(aCx);
738 :
739 0 : if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
740 0 : return;
741 : }
742 :
743 0 : if (!arguments.AppendElement(value, fallible)) {
744 0 : return;
745 : }
746 : }
747 :
748 0 : mConsole->ProfileMethodInternal(aCx, mAction, arguments);
749 : }
750 :
751 : virtual void
752 0 : ReleaseData() override
753 0 : {}
754 :
755 : nsString mAction;
756 :
757 : // This is a reference of the sequence of arguments we receive from the DOM
758 : // bindings and it's rooted by them. It's only used on the owning thread in
759 : // PreDispatch().
760 : const Sequence<JS::Value>& mArguments;
761 : };
762 :
763 : NS_IMPL_CYCLE_COLLECTION_CLASS(Console)
764 :
765 : // We don't need to traverse/unlink mStorage and mSandbox because they are not
766 : // CCed objects and they are only used on the main thread, even when this
767 : // Console object is used on workers.
768 :
769 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Console)
770 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
771 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsoleEventNotifier)
772 0 : tmp->Shutdown();
773 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
774 :
775 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console)
776 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
777 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsoleEventNotifier)
778 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
779 :
780 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console)
781 0 : for (uint32_t i = 0; i < tmp->mCallDataStorage.Length(); ++i) {
782 0 : tmp->mCallDataStorage[i]->Trace(aCallbacks, aClosure);
783 : }
784 :
785 0 : for (uint32_t i = 0; i < tmp->mCallDataStoragePending.Length(); ++i) {
786 0 : tmp->mCallDataStoragePending[i]->Trace(aCallbacks, aClosure);
787 : }
788 :
789 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
790 :
791 4 : NS_IMPL_CYCLE_COLLECTING_ADDREF(Console)
792 3 : NS_IMPL_CYCLE_COLLECTING_RELEASE(Console)
793 :
794 5 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Console)
795 2 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
796 2 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
797 2 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
798 0 : NS_INTERFACE_MAP_END
799 :
800 : /* static */ already_AddRefed<Console>
801 1 : Console::Create(nsPIDOMWindowInner* aWindow, ErrorResult& aRv)
802 : {
803 1 : MOZ_ASSERT_IF(NS_IsMainThread(), aWindow);
804 :
805 2 : RefPtr<Console> console = new Console(aWindow);
806 1 : console->Initialize(aRv);
807 1 : if (NS_WARN_IF(aRv.Failed())) {
808 0 : return nullptr;
809 : }
810 :
811 1 : return console.forget();
812 : }
813 :
814 1 : Console::Console(nsPIDOMWindowInner* aWindow)
815 : : mWindow(aWindow)
816 : , mOuterID(0)
817 : , mInnerID(0)
818 1 : , mStatus(eUnknown)
819 : {
820 1 : MOZ_ASSERT_IF(NS_IsMainThread(), aWindow);
821 :
822 1 : if (mWindow) {
823 1 : MOZ_ASSERT(mWindow->IsInnerWindow());
824 1 : mInnerID = mWindow->WindowID();
825 :
826 : // Without outerwindow any console message coming from this object will not
827 : // shown in the devtools webconsole. But this should be fine because
828 : // probably we are shutting down, or the window is CCed/GCed.
829 1 : nsPIDOMWindowOuter* outerWindow = mWindow->GetOuterWindow();
830 1 : if (outerWindow) {
831 1 : mOuterID = outerWindow->WindowID();
832 : }
833 : }
834 :
835 1 : mozilla::HoldJSObjects(this);
836 1 : }
837 :
838 0 : Console::~Console()
839 : {
840 0 : AssertIsOnOwningThread();
841 0 : Shutdown();
842 0 : mozilla::DropJSObjects(this);
843 0 : }
844 :
845 : void
846 1 : Console::Initialize(ErrorResult& aRv)
847 : {
848 1 : AssertIsOnOwningThread();
849 1 : MOZ_ASSERT(mStatus == eUnknown);
850 :
851 1 : if (NS_IsMainThread()) {
852 2 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
853 1 : if (NS_WARN_IF(!obs)) {
854 0 : aRv.Throw(NS_ERROR_FAILURE);
855 0 : return;
856 : }
857 :
858 1 : aRv = obs->AddObserver(this, "inner-window-destroyed", true);
859 1 : if (NS_WARN_IF(aRv.Failed())) {
860 0 : return;
861 : }
862 :
863 1 : aRv = obs->AddObserver(this, "memory-pressure", true);
864 1 : if (NS_WARN_IF(aRv.Failed())) {
865 0 : return;
866 : }
867 : }
868 :
869 1 : mStatus = eInitialized;
870 : }
871 :
872 : void
873 0 : Console::Shutdown()
874 : {
875 0 : AssertIsOnOwningThread();
876 :
877 0 : if (mStatus == eUnknown || mStatus == eShuttingDown) {
878 0 : return;
879 : }
880 :
881 0 : if (NS_IsMainThread()) {
882 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
883 0 : if (obs) {
884 0 : obs->RemoveObserver(this, "inner-window-destroyed");
885 0 : obs->RemoveObserver(this, "memory-pressure");
886 : }
887 : }
888 :
889 : NS_ReleaseOnMainThread(
890 0 : "Console::mStorage", mStorage.forget());
891 : NS_ReleaseOnMainThread(
892 0 : "Console::mSandbox", mSandbox.forget());
893 :
894 0 : mTimerRegistry.Clear();
895 0 : mCounterRegistry.Clear();
896 :
897 0 : mCallDataStorage.Clear();
898 0 : mCallDataStoragePending.Clear();
899 :
900 0 : mStatus = eShuttingDown;
901 : }
902 :
903 : NS_IMETHODIMP
904 0 : Console::Observe(nsISupports* aSubject, const char* aTopic,
905 : const char16_t* aData)
906 : {
907 0 : AssertIsOnMainThread();
908 :
909 0 : if (!strcmp(aTopic, "inner-window-destroyed")) {
910 0 : nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
911 0 : NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
912 :
913 : uint64_t innerID;
914 0 : nsresult rv = wrapper->GetData(&innerID);
915 0 : NS_ENSURE_SUCCESS(rv, rv);
916 :
917 0 : if (innerID == mInnerID) {
918 0 : Shutdown();
919 : }
920 :
921 0 : return NS_OK;
922 : }
923 :
924 0 : if (!strcmp(aTopic, "memory-pressure")) {
925 0 : ClearStorage();
926 0 : return NS_OK;
927 : }
928 :
929 0 : return NS_OK;
930 : }
931 :
932 : void
933 0 : Console::ClearStorage()
934 : {
935 0 : mCallDataStorage.Clear();
936 0 : }
937 :
938 : #define METHOD(name, string) \
939 : /* static */ void \
940 : Console::name(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData) \
941 : { \
942 : Method(aGlobal, Method##name, NS_LITERAL_STRING(string), aData); \
943 : }
944 :
945 0 : METHOD(Log, "log")
946 0 : METHOD(Info, "info")
947 0 : METHOD(Warn, "warn")
948 1 : METHOD(Error, "error")
949 0 : METHOD(Exception, "exception")
950 0 : METHOD(Debug, "debug")
951 0 : METHOD(Table, "table")
952 0 : METHOD(Trace, "trace")
953 :
954 : /* static */ void
955 0 : Console::Clear(const GlobalObject& aGlobal)
956 : {
957 0 : const Sequence<JS::Value> data;
958 0 : Method(aGlobal, MethodClear, NS_LITERAL_STRING("clear"), data);
959 0 : }
960 :
961 : // Displays an interactive listing of all the properties of an object.
962 0 : METHOD(Dir, "dir");
963 0 : METHOD(Dirxml, "dirxml");
964 :
965 0 : METHOD(Group, "group")
966 0 : METHOD(GroupCollapsed, "groupCollapsed")
967 :
968 : /* static */ void
969 0 : Console::GroupEnd(const GlobalObject& aGlobal)
970 : {
971 0 : const Sequence<JS::Value> data;
972 0 : Method(aGlobal, MethodGroupEnd, NS_LITERAL_STRING("groupEnd"), data);
973 0 : }
974 :
975 : /* static */ void
976 0 : Console::Time(const GlobalObject& aGlobal, const nsAString& aLabel)
977 : {
978 0 : StringMethod(aGlobal, aLabel, MethodTime, NS_LITERAL_STRING("time"));
979 0 : }
980 :
981 : /* static */ void
982 0 : Console::TimeEnd(const GlobalObject& aGlobal, const nsAString& aLabel)
983 : {
984 0 : StringMethod(aGlobal, aLabel, MethodTimeEnd, NS_LITERAL_STRING("timeEnd"));
985 0 : }
986 :
987 : /* static */ void
988 0 : Console::StringMethod(const GlobalObject& aGlobal, const nsAString& aLabel,
989 : MethodName aMethodName, const nsAString& aMethodString)
990 : {
991 0 : JSContext* cx = aGlobal.Context();
992 :
993 0 : ClearException ce(cx);
994 :
995 0 : Sequence<JS::Value> data;
996 0 : SequenceRooter<JS::Value> rooter(cx, &data);
997 :
998 0 : JS::Rooted<JS::Value> value(cx);
999 0 : if (!dom::ToJSValue(cx, aLabel, &value)) {
1000 0 : return;
1001 : }
1002 :
1003 0 : if (!data.AppendElement(value, fallible)) {
1004 0 : return;
1005 : }
1006 :
1007 0 : Method(aGlobal, aMethodName, aMethodString, data);
1008 : }
1009 :
1010 : /* static */ void
1011 0 : Console::TimeStamp(const GlobalObject& aGlobal,
1012 : const JS::Handle<JS::Value> aData)
1013 : {
1014 0 : JSContext* cx = aGlobal.Context();
1015 :
1016 0 : ClearException ce(cx);
1017 :
1018 0 : Sequence<JS::Value> data;
1019 0 : SequenceRooter<JS::Value> rooter(cx, &data);
1020 :
1021 0 : if (aData.isString() && !data.AppendElement(aData, fallible)) {
1022 0 : return;
1023 : }
1024 :
1025 0 : Method(aGlobal, MethodTimeStamp, NS_LITERAL_STRING("timeStamp"), data);
1026 : }
1027 :
1028 : /* static */ void
1029 0 : Console::Profile(const GlobalObject& aGlobal, const Sequence<JS::Value>& aData)
1030 : {
1031 0 : ProfileMethod(aGlobal, NS_LITERAL_STRING("profile"), aData);
1032 0 : }
1033 :
1034 : /* static */ void
1035 0 : Console::ProfileEnd(const GlobalObject& aGlobal,
1036 : const Sequence<JS::Value>& aData)
1037 : {
1038 0 : ProfileMethod(aGlobal, NS_LITERAL_STRING("profileEnd"), aData);
1039 0 : }
1040 :
1041 : /* static */ void
1042 0 : Console::ProfileMethod(const GlobalObject& aGlobal, const nsAString& aAction,
1043 : const Sequence<JS::Value>& aData)
1044 : {
1045 0 : RefPtr<Console> console = GetConsole(aGlobal);
1046 0 : if (!console) {
1047 0 : return;
1048 : }
1049 :
1050 0 : JSContext* cx = aGlobal.Context();
1051 0 : console->ProfileMethodInternal(cx, aAction, aData);
1052 : }
1053 :
1054 : void
1055 0 : Console::ProfileMethodInternal(JSContext* aCx, const nsAString& aAction,
1056 : const Sequence<JS::Value>& aData)
1057 : {
1058 0 : if (!NS_IsMainThread()) {
1059 : // Here we are in a worker thread.
1060 : RefPtr<ConsoleProfileRunnable> runnable =
1061 0 : new ConsoleProfileRunnable(this, aAction, aData);
1062 :
1063 0 : runnable->Dispatch(aCx);
1064 0 : return;
1065 : }
1066 :
1067 0 : ClearException ce(aCx);
1068 :
1069 0 : RootedDictionary<ConsoleProfileEvent> event(aCx);
1070 0 : event.mAction = aAction;
1071 :
1072 0 : event.mArguments.Construct();
1073 0 : Sequence<JS::Value>& sequence = event.mArguments.Value();
1074 :
1075 0 : for (uint32_t i = 0; i < aData.Length(); ++i) {
1076 0 : if (!sequence.AppendElement(aData[i], fallible)) {
1077 0 : return;
1078 : }
1079 : }
1080 :
1081 0 : JS::Rooted<JS::Value> eventValue(aCx);
1082 0 : if (!ToJSValue(aCx, event, &eventValue)) {
1083 0 : return;
1084 : }
1085 :
1086 0 : JS::Rooted<JSObject*> eventObj(aCx, &eventValue.toObject());
1087 0 : MOZ_ASSERT(eventObj);
1088 :
1089 0 : if (!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventValue,
1090 : JSPROP_ENUMERATE)) {
1091 0 : return;
1092 : }
1093 :
1094 0 : nsIXPConnect* xpc = nsContentUtils::XPConnect();
1095 0 : nsCOMPtr<nsISupports> wrapper;
1096 0 : const nsIID& iid = NS_GET_IID(nsISupports);
1097 :
1098 0 : if (NS_FAILED(xpc->WrapJS(aCx, eventObj, iid, getter_AddRefs(wrapper)))) {
1099 0 : return;
1100 : }
1101 :
1102 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
1103 0 : if (obs) {
1104 0 : obs->NotifyObservers(wrapper, "console-api-profiler", nullptr);
1105 : }
1106 : }
1107 :
1108 : /* static */ void
1109 0 : Console::Assert(const GlobalObject& aGlobal, bool aCondition,
1110 : const Sequence<JS::Value>& aData)
1111 : {
1112 0 : if (!aCondition) {
1113 0 : Method(aGlobal, MethodAssert, NS_LITERAL_STRING("assert"), aData);
1114 : }
1115 0 : }
1116 :
1117 : /* static */ void
1118 0 : Console::Count(const GlobalObject& aGlobal, const nsAString& aLabel)
1119 : {
1120 0 : StringMethod(aGlobal, aLabel, MethodCount, NS_LITERAL_STRING("count"));
1121 0 : }
1122 :
1123 : namespace {
1124 :
1125 : nsresult
1126 1 : StackFrameToStackEntry(JSContext* aCx, nsIStackFrame* aStackFrame,
1127 : ConsoleStackEntry& aStackEntry)
1128 : {
1129 1 : MOZ_ASSERT(aStackFrame);
1130 :
1131 1 : nsresult rv = aStackFrame->GetFilename(aCx, aStackEntry.mFilename);
1132 1 : NS_ENSURE_SUCCESS(rv, rv);
1133 :
1134 : int32_t lineNumber;
1135 1 : rv = aStackFrame->GetLineNumber(aCx, &lineNumber);
1136 1 : NS_ENSURE_SUCCESS(rv, rv);
1137 :
1138 1 : aStackEntry.mLineNumber = lineNumber;
1139 :
1140 : int32_t columnNumber;
1141 1 : rv = aStackFrame->GetColumnNumber(aCx, &columnNumber);
1142 1 : NS_ENSURE_SUCCESS(rv, rv);
1143 :
1144 1 : aStackEntry.mColumnNumber = columnNumber;
1145 :
1146 1 : rv = aStackFrame->GetName(aCx, aStackEntry.mFunctionName);
1147 1 : NS_ENSURE_SUCCESS(rv, rv);
1148 :
1149 2 : nsString cause;
1150 1 : rv = aStackFrame->GetAsyncCause(aCx, cause);
1151 1 : NS_ENSURE_SUCCESS(rv, rv);
1152 1 : if (!cause.IsEmpty()) {
1153 0 : aStackEntry.mAsyncCause.Construct(cause);
1154 : }
1155 :
1156 1 : aStackEntry.mLanguage = nsIProgrammingLanguage::JAVASCRIPT;
1157 1 : return NS_OK;
1158 : }
1159 :
1160 : nsresult
1161 0 : ReifyStack(JSContext* aCx, nsIStackFrame* aStack,
1162 : nsTArray<ConsoleStackEntry>& aRefiedStack)
1163 : {
1164 0 : nsCOMPtr<nsIStackFrame> stack(aStack);
1165 :
1166 0 : while (stack) {
1167 0 : ConsoleStackEntry& data = *aRefiedStack.AppendElement();
1168 0 : nsresult rv = StackFrameToStackEntry(aCx, stack, data);
1169 0 : NS_ENSURE_SUCCESS(rv, rv);
1170 :
1171 0 : nsCOMPtr<nsIStackFrame> caller;
1172 0 : rv = stack->GetCaller(aCx, getter_AddRefs(caller));
1173 0 : NS_ENSURE_SUCCESS(rv, rv);
1174 :
1175 0 : if (!caller) {
1176 0 : rv = stack->GetAsyncCaller(aCx, getter_AddRefs(caller));
1177 0 : NS_ENSURE_SUCCESS(rv, rv);
1178 : }
1179 0 : stack.swap(caller);
1180 : }
1181 :
1182 0 : return NS_OK;
1183 : }
1184 :
1185 : } // anonymous namespace
1186 :
1187 : // Queue a call to a console method. See the CALL_DELAY constant.
1188 : /* static */ void
1189 1 : Console::Method(const GlobalObject& aGlobal, MethodName aMethodName,
1190 : const nsAString& aMethodString,
1191 : const Sequence<JS::Value>& aData)
1192 : {
1193 2 : RefPtr<Console> console = GetConsole(aGlobal);
1194 1 : if (!console) {
1195 0 : return;
1196 : }
1197 :
1198 1 : console->MethodInternal(aGlobal.Context(), aMethodName, aMethodString,
1199 1 : aData);
1200 : }
1201 :
1202 : void
1203 1 : Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
1204 : const nsAString& aMethodString,
1205 : const Sequence<JS::Value>& aData)
1206 : {
1207 1 : AssertIsOnOwningThread();
1208 :
1209 1 : RefPtr<ConsoleCallData> callData(new ConsoleCallData());
1210 :
1211 1 : ClearException ce(aCx);
1212 :
1213 1 : if (NS_WARN_IF(!callData->Initialize(aCx, aMethodName, aMethodString,
1214 : aData, this))) {
1215 0 : return;
1216 : }
1217 :
1218 1 : OriginAttributes oa;
1219 :
1220 1 : if (mWindow) {
1221 : // Save the principal's OriginAttributes in the console event data
1222 : // so that we will be able to filter messages by origin attributes.
1223 2 : nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
1224 1 : if (NS_WARN_IF(!sop)) {
1225 0 : return;
1226 : }
1227 :
1228 2 : nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
1229 1 : if (NS_WARN_IF(!principal)) {
1230 0 : return;
1231 : }
1232 :
1233 1 : oa = principal->OriginAttributesRef();
1234 1 : callData->SetAddonId(principal);
1235 :
1236 : #ifdef DEBUG
1237 1 : if (!nsContentUtils::IsSystemPrincipal(principal)) {
1238 2 : nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
1239 1 : if (webNav) {
1240 2 : nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
1241 1 : MOZ_ASSERT(loadContext);
1242 :
1243 : bool pb;
1244 1 : if (NS_SUCCEEDED(loadContext->GetUsePrivateBrowsing(&pb))) {
1245 1 : MOZ_ASSERT(pb == !!oa.mPrivateBrowsingId);
1246 : }
1247 : }
1248 : }
1249 : #endif
1250 : } else {
1251 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1252 0 : MOZ_ASSERT(workerPrivate);
1253 0 : oa = workerPrivate->GetOriginAttributes();
1254 : }
1255 :
1256 1 : callData->SetOriginAttributes(oa);
1257 :
1258 1 : JS::StackCapture captureMode = ShouldIncludeStackTrace(aMethodName) ?
1259 3 : JS::StackCapture(JS::MaxFrames(DEFAULT_MAX_STACKTRACE_DEPTH)) :
1260 2 : JS::StackCapture(JS::FirstSubsumedFrame(aCx));
1261 1 : nsCOMPtr<nsIStackFrame> stack = CreateStack(aCx, mozilla::Move(captureMode));
1262 :
1263 1 : if (stack) {
1264 1 : callData->mTopStackFrame.emplace();
1265 1 : nsresult rv = StackFrameToStackEntry(aCx, stack,
1266 2 : *callData->mTopStackFrame);
1267 1 : if (NS_FAILED(rv)) {
1268 0 : return;
1269 : }
1270 : }
1271 :
1272 1 : if (NS_IsMainThread()) {
1273 1 : callData->mStack = stack;
1274 : } else {
1275 : // nsIStackFrame is not threadsafe, so we need to snapshot it now,
1276 : // before we post our runnable to the main thread.
1277 0 : callData->mReifiedStack.emplace();
1278 0 : nsresult rv = ReifyStack(aCx, stack, *callData->mReifiedStack);
1279 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1280 0 : return;
1281 : }
1282 : }
1283 :
1284 : DOMHighResTimeStamp monotonicTimer;
1285 :
1286 : // Monotonic timer for 'time' and 'timeEnd'
1287 1 : if (aMethodName == MethodTime ||
1288 1 : aMethodName == MethodTimeEnd ||
1289 : aMethodName == MethodTimeStamp) {
1290 0 : if (mWindow) {
1291 0 : nsGlobalWindow *win = nsGlobalWindow::Cast(mWindow);
1292 0 : MOZ_ASSERT(win);
1293 :
1294 0 : RefPtr<Performance> performance = win->GetPerformance();
1295 0 : if (!performance) {
1296 0 : return;
1297 : }
1298 :
1299 0 : monotonicTimer = performance->Now();
1300 :
1301 0 : nsDocShell* docShell = static_cast<nsDocShell*>(mWindow->GetDocShell());
1302 0 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
1303 0 : bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
1304 :
1305 : // The 'timeStamp' recordings do not need an argument; use empty string
1306 : // if no arguments passed in.
1307 0 : if (isTimelineRecording && aMethodName == MethodTimeStamp) {
1308 0 : JS::Rooted<JS::Value> value(aCx, aData.Length() == 0
1309 0 : ? JS_GetEmptyStringValue(aCx)
1310 0 : : aData[0]);
1311 0 : JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
1312 0 : if (!jsString) {
1313 0 : return;
1314 : }
1315 :
1316 0 : nsAutoJSString key;
1317 0 : if (!key.init(aCx, jsString)) {
1318 0 : return;
1319 : }
1320 :
1321 0 : timelines->AddMarkerForDocShell(docShell, Move(
1322 0 : MakeUnique<TimestampTimelineMarker>(key)));
1323 : }
1324 : // For `console.time(foo)` and `console.timeEnd(foo)`.
1325 0 : else if (isTimelineRecording && aData.Length() == 1) {
1326 0 : JS::Rooted<JS::Value> value(aCx, aData[0]);
1327 0 : JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
1328 0 : if (!jsString) {
1329 0 : return;
1330 : }
1331 :
1332 0 : nsAutoJSString key;
1333 0 : if (!key.init(aCx, jsString)) {
1334 0 : return;
1335 : }
1336 :
1337 0 : timelines->AddMarkerForDocShell(docShell, Move(
1338 0 : MakeUnique<ConsoleTimelineMarker>(
1339 0 : key, aMethodName == MethodTime ? MarkerTracingType::START
1340 0 : : MarkerTracingType::END)));
1341 : }
1342 : } else {
1343 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1344 0 : MOZ_ASSERT(workerPrivate);
1345 :
1346 0 : monotonicTimer = workerPrivate->TimeStampToDOMHighRes(TimeStamp::Now());
1347 : }
1348 : }
1349 :
1350 1 : if (aMethodName == MethodTime && !aData.IsEmpty()) {
1351 0 : callData->mStartTimerStatus = StartTimer(aCx, aData[0],
1352 : monotonicTimer,
1353 0 : callData->mStartTimerLabel,
1354 0 : &callData->mStartTimerValue);
1355 : }
1356 :
1357 1 : else if (aMethodName == MethodTimeEnd && !aData.IsEmpty()) {
1358 0 : callData->mStopTimerStatus = StopTimer(aCx, aData[0],
1359 : monotonicTimer,
1360 0 : callData->mStopTimerLabel,
1361 0 : &callData->mStopTimerDuration);
1362 : }
1363 :
1364 1 : else if (aMethodName == MethodCount) {
1365 0 : callData->mCountValue = IncreaseCounter(aCx, aData, callData->mCountLabel);
1366 0 : if (!callData->mCountValue) {
1367 0 : return;
1368 : }
1369 : }
1370 :
1371 1 : if (NS_IsMainThread()) {
1372 1 : callData->SetIDs(mOuterID, mInnerID);
1373 1 : ProcessCallData(aCx, callData, aData);
1374 :
1375 : // Just because we don't want to expose
1376 : // retrieveConsoleEvents/setConsoleEventHandler to main-thread, we can
1377 : // cleanup the mCallDataStorage:
1378 1 : UnstoreCallData(callData);
1379 1 : return;
1380 : }
1381 :
1382 : // We do this only in workers for now.
1383 0 : NotifyHandler(aCx, aData, callData);
1384 :
1385 : RefPtr<ConsoleCallDataRunnable> runnable =
1386 0 : new ConsoleCallDataRunnable(this, callData);
1387 0 : Unused << NS_WARN_IF(!runnable->Dispatch(aCx));
1388 : }
1389 :
1390 : // We store information to lazily compute the stack in the reserved slots of
1391 : // LazyStackGetter. The first slot always stores a JS object: it's either the
1392 : // JS wrapper of the nsIStackFrame or the actual reified stack representation.
1393 : // The second slot is a PrivateValue() holding an nsIStackFrame* when we haven't
1394 : // reified the stack yet, or an UndefinedValue() otherwise.
1395 : enum {
1396 : SLOT_STACKOBJ,
1397 : SLOT_RAW_STACK
1398 : };
1399 :
1400 : bool
1401 0 : LazyStackGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
1402 : {
1403 0 : JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
1404 0 : JS::Rooted<JSObject*> callee(aCx, &args.callee());
1405 :
1406 0 : JS::Value v = js::GetFunctionNativeReserved(&args.callee(), SLOT_RAW_STACK);
1407 0 : if (v.isUndefined()) {
1408 : // Already reified.
1409 0 : args.rval().set(js::GetFunctionNativeReserved(callee, SLOT_STACKOBJ));
1410 0 : return true;
1411 : }
1412 :
1413 0 : nsIStackFrame* stack = reinterpret_cast<nsIStackFrame*>(v.toPrivate());
1414 0 : nsTArray<ConsoleStackEntry> reifiedStack;
1415 0 : nsresult rv = ReifyStack(aCx, stack, reifiedStack);
1416 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1417 0 : Throw(aCx, rv);
1418 0 : return false;
1419 : }
1420 :
1421 0 : JS::Rooted<JS::Value> stackVal(aCx);
1422 0 : if (NS_WARN_IF(!ToJSValue(aCx, reifiedStack, &stackVal))) {
1423 0 : return false;
1424 : }
1425 :
1426 0 : MOZ_ASSERT(stackVal.isObject());
1427 :
1428 0 : js::SetFunctionNativeReserved(callee, SLOT_STACKOBJ, stackVal);
1429 0 : js::SetFunctionNativeReserved(callee, SLOT_RAW_STACK, JS::UndefinedValue());
1430 :
1431 0 : args.rval().set(stackVal);
1432 0 : return true;
1433 : }
1434 :
1435 : void
1436 1 : Console::ProcessCallData(JSContext* aCx, ConsoleCallData* aData,
1437 : const Sequence<JS::Value>& aArguments)
1438 : {
1439 1 : AssertIsOnMainThread();
1440 1 : MOZ_ASSERT(aData);
1441 :
1442 2 : JS::Rooted<JS::Value> eventValue(aCx);
1443 :
1444 : // We want to create a console event object and pass it to our
1445 : // nsIConsoleAPIStorage implementation. We want to define some accessor
1446 : // properties on this object, and those will need to keep an nsIStackFrame
1447 : // alive. But nsIStackFrame cannot be wrapped in an untrusted scope. And
1448 : // further, passing untrusted objects to system code is likely to run afoul of
1449 : // Object Xrays. So we want to wrap in a system-principal scope here. But
1450 : // which one? We could cheat and try to get the underlying JSObject* of
1451 : // mStorage, but that's a bit fragile. Instead, we just use the junk scope,
1452 : // with explicit permission from the XPConnect module owner. If you're
1453 : // tempted to do that anywhere else, talk to said module owner first.
1454 :
1455 : // aCx and aArguments are in the same compartment.
1456 1 : if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(aCx, aArguments,
1457 : xpc::PrivilegedJunkScope(),
1458 : &eventValue, aData))) {
1459 0 : return;
1460 : }
1461 :
1462 1 : if (!mStorage) {
1463 1 : mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
1464 : }
1465 :
1466 1 : if (!mStorage) {
1467 0 : NS_WARNING("Failed to get the ConsoleAPIStorage service.");
1468 0 : return;
1469 : }
1470 :
1471 2 : nsAutoString innerID, outerID;
1472 :
1473 1 : MOZ_ASSERT(aData->mIDType != ConsoleCallData::eUnknown);
1474 1 : if (aData->mIDType == ConsoleCallData::eString) {
1475 0 : outerID = aData->mOuterIDString;
1476 0 : innerID = aData->mInnerIDString;
1477 : } else {
1478 1 : MOZ_ASSERT(aData->mIDType == ConsoleCallData::eNumber);
1479 1 : outerID.AppendInt(aData->mOuterIDNumber);
1480 1 : innerID.AppendInt(aData->mInnerIDNumber);
1481 : }
1482 :
1483 1 : if (aData->mMethodName == MethodClear) {
1484 0 : DebugOnly<nsresult> rv = mStorage->ClearEvents(innerID);
1485 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "ClearEvents failed");
1486 : }
1487 :
1488 1 : if (NS_FAILED(mStorage->RecordEvent(innerID, outerID, eventValue))) {
1489 0 : NS_WARNING("Failed to record a console event.");
1490 : }
1491 : }
1492 :
1493 : bool
1494 1 : Console::PopulateConsoleNotificationInTheTargetScope(JSContext* aCx,
1495 : const Sequence<JS::Value>& aArguments,
1496 : JSObject* aTargetScope,
1497 : JS::MutableHandle<JS::Value> aEventValue,
1498 : ConsoleCallData* aData)
1499 : {
1500 1 : MOZ_ASSERT(aCx);
1501 1 : MOZ_ASSERT(aData);
1502 1 : MOZ_ASSERT(aTargetScope);
1503 :
1504 2 : JS::Rooted<JSObject*> targetScope(aCx, aTargetScope);
1505 :
1506 2 : ConsoleStackEntry frame;
1507 1 : if (aData->mTopStackFrame) {
1508 1 : frame = *aData->mTopStackFrame;
1509 : }
1510 :
1511 2 : ClearException ce(aCx);
1512 2 : RootedDictionary<ConsoleEvent> event(aCx);
1513 :
1514 : // Save the principal's OriginAttributes in the console event data
1515 : // so that we will be able to filter messages by origin attributes.
1516 2 : JS::Rooted<JS::Value> originAttributesValue(aCx);
1517 1 : if (ToJSValue(aCx, aData->mOriginAttributes, &originAttributesValue)) {
1518 1 : event.mOriginAttributes = originAttributesValue;
1519 : }
1520 :
1521 1 : event.mAddonId = aData->mAddonId;
1522 :
1523 1 : event.mID.Construct();
1524 1 : event.mInnerID.Construct();
1525 :
1526 1 : if (aData->mIDType == ConsoleCallData::eString) {
1527 0 : event.mID.Value().SetAsString() = aData->mOuterIDString;
1528 0 : event.mInnerID.Value().SetAsString() = aData->mInnerIDString;
1529 1 : } else if (aData->mIDType == ConsoleCallData::eNumber) {
1530 1 : event.mID.Value().SetAsUnsignedLongLong() = aData->mOuterIDNumber;
1531 1 : event.mInnerID.Value().SetAsUnsignedLongLong() = aData->mInnerIDNumber;
1532 : } else {
1533 : // aData->mIDType can be eUnknown when we dispatch notifications via
1534 : // mConsoleEventNotifier.
1535 0 : event.mID.Value().SetAsUnsignedLongLong() = 0;
1536 0 : event.mInnerID.Value().SetAsUnsignedLongLong() = 0;
1537 : }
1538 :
1539 1 : event.mLevel = aData->mMethodString;
1540 1 : event.mFilename = frame.mFilename;
1541 :
1542 2 : nsCOMPtr<nsIURI> filenameURI;
1543 2 : nsAutoCString pass;
1544 5 : if (NS_IsMainThread() &&
1545 5 : NS_SUCCEEDED(NS_NewURI(getter_AddRefs(filenameURI), frame.mFilename)) &&
1546 4 : NS_SUCCEEDED(filenameURI->GetPassword(pass)) && !pass.IsEmpty()) {
1547 0 : nsCOMPtr<nsISensitiveInfoHiddenURI> safeURI = do_QueryInterface(filenameURI);
1548 0 : nsAutoCString spec;
1549 0 : if (safeURI &&
1550 0 : NS_SUCCEEDED(safeURI->GetSensitiveInfoHiddenSpec(spec))) {
1551 0 : CopyUTF8toUTF16(spec, event.mFilename);
1552 : }
1553 : }
1554 :
1555 1 : event.mLineNumber = frame.mLineNumber;
1556 1 : event.mColumnNumber = frame.mColumnNumber;
1557 1 : event.mFunctionName = frame.mFunctionName;
1558 1 : event.mTimeStamp = aData->mTimeStamp;
1559 1 : event.mPrivate = !!aData->mOriginAttributes.mPrivateBrowsingId;
1560 :
1561 1 : switch (aData->mMethodName) {
1562 : case MethodLog:
1563 : case MethodInfo:
1564 : case MethodWarn:
1565 : case MethodError:
1566 : case MethodException:
1567 : case MethodDebug:
1568 : case MethodAssert:
1569 : case MethodGroup:
1570 : case MethodGroupCollapsed:
1571 1 : event.mArguments.Construct();
1572 1 : event.mStyles.Construct();
1573 1 : if (NS_WARN_IF(!ProcessArguments(aCx, aArguments,
1574 : event.mArguments.Value(),
1575 : event.mStyles.Value()))) {
1576 0 : return false;
1577 : }
1578 :
1579 1 : break;
1580 :
1581 : default:
1582 0 : event.mArguments.Construct();
1583 0 : if (NS_WARN_IF(!ArgumentsToValueList(aArguments,
1584 : event.mArguments.Value()))) {
1585 0 : return false;
1586 : }
1587 : }
1588 :
1589 2 : if (aData->mMethodName == MethodGroup ||
1590 1 : aData->mMethodName == MethodGroupCollapsed) {
1591 0 : ComposeAndStoreGroupName(aCx, event.mArguments.Value(), event.mGroupName);
1592 : }
1593 :
1594 1 : else if (aData->mMethodName == MethodGroupEnd) {
1595 0 : if (!UnstoreGroupName(event.mGroupName)) {
1596 0 : return false;
1597 : }
1598 : }
1599 :
1600 1 : else if (aData->mMethodName == MethodTime && !aArguments.IsEmpty()) {
1601 : event.mTimer = CreateStartTimerValue(aCx, aData->mStartTimerLabel,
1602 0 : aData->mStartTimerStatus);
1603 : }
1604 :
1605 1 : else if (aData->mMethodName == MethodTimeEnd && !aArguments.IsEmpty()) {
1606 : event.mTimer = CreateStopTimerValue(aCx, aData->mStopTimerLabel,
1607 : aData->mStopTimerDuration,
1608 0 : aData->mStopTimerStatus);
1609 : }
1610 :
1611 1 : else if (aData->mMethodName == MethodCount) {
1612 : event.mCounter = CreateCounterValue(aCx, aData->mCountLabel,
1613 0 : aData->mCountValue);
1614 : }
1615 :
1616 2 : JSAutoCompartment ac2(aCx, targetScope);
1617 :
1618 1 : if (NS_WARN_IF(!ToJSValue(aCx, event, aEventValue))) {
1619 0 : return false;
1620 : }
1621 :
1622 2 : JS::Rooted<JSObject*> eventObj(aCx, &aEventValue.toObject());
1623 1 : if (NS_WARN_IF(!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventObj,
1624 : JSPROP_ENUMERATE))) {
1625 0 : return false;
1626 : }
1627 :
1628 1 : if (ShouldIncludeStackTrace(aData->mMethodName)) {
1629 : // Now define the "stacktrace" property on eventObj. There are two cases
1630 : // here. Either we came from a worker and have a reified stack, or we want
1631 : // to define a getter that will lazily reify the stack.
1632 1 : if (aData->mReifiedStack) {
1633 0 : JS::Rooted<JS::Value> stacktrace(aCx);
1634 0 : if (NS_WARN_IF(!ToJSValue(aCx, *aData->mReifiedStack, &stacktrace)) ||
1635 0 : NS_WARN_IF(!JS_DefineProperty(aCx, eventObj, "stacktrace", stacktrace,
1636 : JSPROP_ENUMERATE))) {
1637 0 : return false;
1638 : }
1639 : } else {
1640 1 : JSFunction* fun = js::NewFunctionWithReserved(aCx, LazyStackGetter, 0, 0,
1641 1 : "stacktrace");
1642 1 : if (NS_WARN_IF(!fun)) {
1643 0 : return false;
1644 : }
1645 :
1646 2 : JS::Rooted<JSObject*> funObj(aCx, JS_GetFunctionObject(fun));
1647 :
1648 : // We want to store our stack in the function and have it stay alive. But
1649 : // we also need sane access to the C++ nsIStackFrame. So store both a JS
1650 : // wrapper and the raw pointer: the former will keep the latter alive.
1651 2 : JS::Rooted<JS::Value> stackVal(aCx);
1652 3 : nsresult rv = nsContentUtils::WrapNative(aCx, aData->mStack,
1653 2 : &stackVal);
1654 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
1655 0 : return false;
1656 : }
1657 :
1658 1 : js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal);
1659 1 : js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK,
1660 2 : JS::PrivateValue(aData->mStack.get()));
1661 :
1662 1 : if (NS_WARN_IF(!JS_DefineProperty(aCx, eventObj, "stacktrace",
1663 : JS::UndefinedHandleValue,
1664 : JSPROP_ENUMERATE | JSPROP_SHARED |
1665 : JSPROP_GETTER | JSPROP_SETTER,
1666 : JS_DATA_TO_FUNC_PTR(JSNative, funObj.get()),
1667 : nullptr))) {
1668 0 : return false;
1669 : }
1670 : }
1671 : }
1672 :
1673 1 : return true;
1674 : }
1675 :
1676 : namespace {
1677 :
1678 : // Helper method for ProcessArguments. Flushes output, if non-empty, to aSequence.
1679 : bool
1680 0 : FlushOutput(JSContext* aCx, Sequence<JS::Value>& aSequence, nsString &aOutput)
1681 : {
1682 0 : if (!aOutput.IsEmpty()) {
1683 0 : JS::Rooted<JSString*> str(aCx, JS_NewUCStringCopyN(aCx,
1684 : aOutput.get(),
1685 0 : aOutput.Length()));
1686 0 : if (NS_WARN_IF(!str)) {
1687 0 : return false;
1688 : }
1689 :
1690 0 : if (NS_WARN_IF(!aSequence.AppendElement(JS::StringValue(str), fallible))) {
1691 0 : return false;
1692 : }
1693 :
1694 0 : aOutput.Truncate();
1695 : }
1696 :
1697 0 : return true;
1698 : }
1699 :
1700 : } // namespace
1701 :
1702 : bool
1703 1 : Console::ProcessArguments(JSContext* aCx,
1704 : const Sequence<JS::Value>& aData,
1705 : Sequence<JS::Value>& aSequence,
1706 : Sequence<nsString>& aStyles) const
1707 : {
1708 1 : if (aData.IsEmpty()) {
1709 0 : return true;
1710 : }
1711 :
1712 1 : if (aData.Length() == 1 || !aData[0].isString()) {
1713 1 : return ArgumentsToValueList(aData, aSequence);
1714 : }
1715 :
1716 0 : JS::Rooted<JS::Value> format(aCx, aData[0]);
1717 0 : JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, format));
1718 0 : if (NS_WARN_IF(!jsString)) {
1719 0 : return false;
1720 : }
1721 :
1722 0 : nsAutoJSString string;
1723 0 : if (NS_WARN_IF(!string.init(aCx, jsString))) {
1724 0 : return false;
1725 : }
1726 :
1727 0 : nsString::const_iterator start, end;
1728 0 : string.BeginReading(start);
1729 0 : string.EndReading(end);
1730 :
1731 0 : nsString output;
1732 0 : uint32_t index = 1;
1733 :
1734 0 : while (start != end) {
1735 0 : if (*start != '%') {
1736 0 : output.Append(*start);
1737 0 : ++start;
1738 0 : continue;
1739 : }
1740 :
1741 0 : ++start;
1742 0 : if (start == end) {
1743 0 : output.Append('%');
1744 0 : break;
1745 : }
1746 :
1747 0 : if (*start == '%') {
1748 0 : output.Append(*start);
1749 0 : ++start;
1750 0 : continue;
1751 : }
1752 :
1753 0 : nsAutoString tmp;
1754 0 : tmp.Append('%');
1755 :
1756 0 : int32_t integer = -1;
1757 0 : int32_t mantissa = -1;
1758 :
1759 : // Let's parse %<number>.<number> for %d and %f
1760 0 : if (*start >= '0' && *start <= '9') {
1761 0 : integer = 0;
1762 :
1763 0 : do {
1764 0 : integer = integer * 10 + *start - '0';
1765 0 : tmp.Append(*start);
1766 0 : ++start;
1767 0 : } while (*start >= '0' && *start <= '9' && start != end);
1768 : }
1769 :
1770 0 : if (start == end) {
1771 0 : output.Append(tmp);
1772 0 : break;
1773 : }
1774 :
1775 0 : if (*start == '.') {
1776 0 : tmp.Append(*start);
1777 0 : ++start;
1778 :
1779 0 : if (start == end) {
1780 0 : output.Append(tmp);
1781 0 : break;
1782 : }
1783 :
1784 : // '.' must be followed by a number.
1785 0 : if (*start < '0' || *start > '9') {
1786 0 : output.Append(tmp);
1787 0 : continue;
1788 : }
1789 :
1790 0 : mantissa = 0;
1791 :
1792 0 : do {
1793 0 : mantissa = mantissa * 10 + *start - '0';
1794 0 : tmp.Append(*start);
1795 0 : ++start;
1796 0 : } while (*start >= '0' && *start <= '9' && start != end);
1797 :
1798 0 : if (start == end) {
1799 0 : output.Append(tmp);
1800 0 : break;
1801 : }
1802 : }
1803 :
1804 0 : char ch = *start;
1805 0 : tmp.Append(ch);
1806 0 : ++start;
1807 :
1808 0 : switch (ch) {
1809 : case 'o':
1810 : case 'O':
1811 : {
1812 0 : if (NS_WARN_IF(!FlushOutput(aCx, aSequence, output))) {
1813 0 : return false;
1814 : }
1815 :
1816 0 : JS::Rooted<JS::Value> v(aCx);
1817 0 : if (index < aData.Length()) {
1818 0 : v = aData[index++];
1819 : }
1820 :
1821 0 : if (NS_WARN_IF(!aSequence.AppendElement(v, fallible))) {
1822 0 : return false;
1823 : }
1824 :
1825 0 : break;
1826 : }
1827 :
1828 : case 'c':
1829 : {
1830 : // If there isn't any output but there's already a style, then
1831 : // discard the previous style and use the next one instead.
1832 0 : if (output.IsEmpty() && !aStyles.IsEmpty()) {
1833 0 : aStyles.TruncateLength(aStyles.Length() - 1);
1834 : }
1835 :
1836 0 : if (NS_WARN_IF(!FlushOutput(aCx, aSequence, output))) {
1837 0 : return false;
1838 : }
1839 :
1840 0 : if (index < aData.Length()) {
1841 0 : JS::Rooted<JS::Value> v(aCx, aData[index++]);
1842 0 : JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, v));
1843 0 : if (NS_WARN_IF(!jsString)) {
1844 0 : return false;
1845 : }
1846 :
1847 0 : int32_t diff = aSequence.Length() - aStyles.Length();
1848 0 : if (diff > 0) {
1849 0 : for (int32_t i = 0; i < diff; i++) {
1850 0 : if (NS_WARN_IF(!aStyles.AppendElement(NullString(), fallible))) {
1851 0 : return false;
1852 : }
1853 : }
1854 : }
1855 :
1856 0 : nsAutoJSString string;
1857 0 : if (NS_WARN_IF(!string.init(aCx, jsString))) {
1858 0 : return false;
1859 : }
1860 :
1861 0 : if (NS_WARN_IF(!aStyles.AppendElement(string, fallible))) {
1862 0 : return false;
1863 : }
1864 : }
1865 0 : break;
1866 : }
1867 :
1868 : case 's':
1869 0 : if (index < aData.Length()) {
1870 0 : JS::Rooted<JS::Value> value(aCx, aData[index++]);
1871 0 : JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
1872 0 : if (NS_WARN_IF(!jsString)) {
1873 0 : return false;
1874 : }
1875 :
1876 0 : nsAutoJSString v;
1877 0 : if (NS_WARN_IF(!v.init(aCx, jsString))) {
1878 0 : return false;
1879 : }
1880 :
1881 0 : output.Append(v);
1882 : }
1883 0 : break;
1884 :
1885 : case 'd':
1886 : case 'i':
1887 0 : if (index < aData.Length()) {
1888 0 : JS::Rooted<JS::Value> value(aCx, aData[index++]);
1889 :
1890 : int32_t v;
1891 0 : if (NS_WARN_IF(!JS::ToInt32(aCx, value, &v))) {
1892 0 : return false;
1893 : }
1894 :
1895 0 : nsCString format;
1896 0 : MakeFormatString(format, integer, mantissa, 'd');
1897 0 : output.AppendPrintf(format.get(), v);
1898 : }
1899 0 : break;
1900 :
1901 : case 'f':
1902 0 : if (index < aData.Length()) {
1903 0 : JS::Rooted<JS::Value> value(aCx, aData[index++]);
1904 :
1905 : double v;
1906 0 : if (NS_WARN_IF(!JS::ToNumber(aCx, value, &v))) {
1907 0 : return false;
1908 : }
1909 :
1910 : // nspr returns "nan", but we want to expose it as "NaN"
1911 0 : if (std::isnan(v)) {
1912 0 : output.AppendFloat(v);
1913 : } else {
1914 0 : nsCString format;
1915 0 : MakeFormatString(format, integer, mantissa, 'f');
1916 0 : output.AppendPrintf(format.get(), v);
1917 : }
1918 : }
1919 0 : break;
1920 :
1921 : default:
1922 0 : output.Append(tmp);
1923 0 : break;
1924 : }
1925 : }
1926 :
1927 0 : if (NS_WARN_IF(!FlushOutput(aCx, aSequence, output))) {
1928 0 : return false;
1929 : }
1930 :
1931 : // Discard trailing style element if there is no output to apply it to.
1932 0 : if (aStyles.Length() > aSequence.Length()) {
1933 0 : aStyles.TruncateLength(aSequence.Length());
1934 : }
1935 :
1936 : // The rest of the array, if unused by the format string.
1937 0 : for (; index < aData.Length(); ++index) {
1938 0 : if (NS_WARN_IF(!aSequence.AppendElement(aData[index], fallible))) {
1939 0 : return false;
1940 : }
1941 : }
1942 :
1943 0 : return true;
1944 : }
1945 :
1946 : void
1947 0 : Console::MakeFormatString(nsCString& aFormat, int32_t aInteger,
1948 : int32_t aMantissa, char aCh) const
1949 : {
1950 0 : aFormat.Append('%');
1951 0 : if (aInteger >= 0) {
1952 0 : aFormat.AppendInt(aInteger);
1953 : }
1954 :
1955 0 : if (aMantissa >= 0) {
1956 0 : aFormat.Append('.');
1957 0 : aFormat.AppendInt(aMantissa);
1958 : }
1959 :
1960 0 : aFormat.Append(aCh);
1961 0 : }
1962 :
1963 : void
1964 0 : Console::ComposeAndStoreGroupName(JSContext* aCx,
1965 : const Sequence<JS::Value>& aData,
1966 : nsAString& aName)
1967 : {
1968 0 : for (uint32_t i = 0; i < aData.Length(); ++i) {
1969 0 : if (i != 0) {
1970 0 : aName.AppendASCII(" ");
1971 : }
1972 :
1973 0 : JS::Rooted<JS::Value> value(aCx, aData[i]);
1974 0 : JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
1975 0 : if (!jsString) {
1976 0 : return;
1977 : }
1978 :
1979 0 : nsAutoJSString string;
1980 0 : if (!string.init(aCx, jsString)) {
1981 0 : return;
1982 : }
1983 :
1984 0 : aName.Append(string);
1985 : }
1986 :
1987 0 : mGroupStack.AppendElement(aName);
1988 : }
1989 :
1990 : bool
1991 0 : Console::UnstoreGroupName(nsAString& aName)
1992 : {
1993 0 : if (mGroupStack.IsEmpty()) {
1994 0 : return false;
1995 : }
1996 :
1997 0 : uint32_t pos = mGroupStack.Length() - 1;
1998 0 : aName = mGroupStack[pos];
1999 0 : mGroupStack.RemoveElementAt(pos);
2000 0 : return true;
2001 : }
2002 :
2003 : Console::TimerStatus
2004 0 : Console::StartTimer(JSContext* aCx, const JS::Value& aName,
2005 : DOMHighResTimeStamp aTimestamp,
2006 : nsAString& aTimerLabel,
2007 : DOMHighResTimeStamp* aTimerValue)
2008 : {
2009 0 : AssertIsOnOwningThread();
2010 0 : MOZ_ASSERT(aTimerValue);
2011 :
2012 0 : *aTimerValue = 0;
2013 :
2014 0 : if (NS_WARN_IF(mTimerRegistry.Count() >= MAX_PAGE_TIMERS)) {
2015 0 : return eTimerMaxReached;
2016 : }
2017 :
2018 0 : JS::Rooted<JS::Value> name(aCx, aName);
2019 0 : JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
2020 0 : if (NS_WARN_IF(!jsString)) {
2021 0 : return eTimerJSException;
2022 : }
2023 :
2024 0 : nsAutoJSString label;
2025 0 : if (NS_WARN_IF(!label.init(aCx, jsString))) {
2026 0 : return eTimerJSException;
2027 : }
2028 :
2029 0 : aTimerLabel = label;
2030 :
2031 0 : auto entry = mTimerRegistry.LookupForAdd(label);
2032 0 : if (entry) {
2033 0 : return eTimerAlreadyExists;
2034 : }
2035 0 : entry.OrInsert([&aTimestamp](){ return aTimestamp; });
2036 :
2037 0 : *aTimerValue = aTimestamp;
2038 0 : return eTimerDone;
2039 : }
2040 :
2041 : JS::Value
2042 0 : Console::CreateStartTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
2043 : TimerStatus aTimerStatus) const
2044 : {
2045 0 : MOZ_ASSERT(aTimerStatus != eTimerUnknown);
2046 :
2047 0 : if (aTimerStatus != eTimerDone) {
2048 0 : return CreateTimerError(aCx, aTimerLabel, aTimerStatus);
2049 : }
2050 :
2051 0 : RootedDictionary<ConsoleTimerStart> timer(aCx);
2052 :
2053 0 : timer.mName = aTimerLabel;
2054 :
2055 0 : JS::Rooted<JS::Value> value(aCx);
2056 0 : if (!ToJSValue(aCx, timer, &value)) {
2057 0 : return JS::UndefinedValue();
2058 : }
2059 :
2060 0 : return value;
2061 : }
2062 :
2063 : Console::TimerStatus
2064 0 : Console::StopTimer(JSContext* aCx, const JS::Value& aName,
2065 : DOMHighResTimeStamp aTimestamp,
2066 : nsAString& aTimerLabel,
2067 : double* aTimerDuration)
2068 : {
2069 0 : AssertIsOnOwningThread();
2070 0 : MOZ_ASSERT(aTimerDuration);
2071 :
2072 0 : *aTimerDuration = 0;
2073 :
2074 0 : JS::Rooted<JS::Value> name(aCx, aName);
2075 0 : JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
2076 0 : if (NS_WARN_IF(!jsString)) {
2077 0 : return eTimerJSException;
2078 : }
2079 :
2080 0 : nsAutoJSString key;
2081 0 : if (NS_WARN_IF(!key.init(aCx, jsString))) {
2082 0 : return eTimerJSException;
2083 : }
2084 :
2085 0 : aTimerLabel = key;
2086 :
2087 0 : DOMHighResTimeStamp value = 0;
2088 0 : if (!mTimerRegistry.Remove(key, &value)) {
2089 0 : NS_WARNING("mTimerRegistry entry not found");
2090 0 : return eTimerDoesntExist;
2091 : }
2092 :
2093 0 : *aTimerDuration = aTimestamp - value;
2094 0 : return eTimerDone;
2095 : }
2096 :
2097 : JS::Value
2098 0 : Console::CreateStopTimerValue(JSContext* aCx, const nsAString& aLabel,
2099 : double aDuration, TimerStatus aStatus) const
2100 : {
2101 0 : if (aStatus != eTimerDone) {
2102 0 : return CreateTimerError(aCx, aLabel, aStatus);
2103 : }
2104 :
2105 0 : RootedDictionary<ConsoleTimerEnd> timer(aCx);
2106 0 : timer.mName = aLabel;
2107 0 : timer.mDuration = aDuration;
2108 :
2109 0 : JS::Rooted<JS::Value> value(aCx);
2110 0 : if (!ToJSValue(aCx, timer, &value)) {
2111 0 : return JS::UndefinedValue();
2112 : }
2113 :
2114 0 : return value;
2115 : }
2116 :
2117 : JS::Value
2118 0 : Console::CreateTimerError(JSContext* aCx, const nsAString& aLabel,
2119 : TimerStatus aStatus) const
2120 : {
2121 0 : MOZ_ASSERT(aStatus != eTimerUnknown && aStatus != eTimerDone);
2122 :
2123 0 : RootedDictionary<ConsoleTimerError> error(aCx);
2124 :
2125 0 : error.mName = aLabel;
2126 :
2127 0 : switch (aStatus) {
2128 : case eTimerAlreadyExists:
2129 0 : error.mError.AssignLiteral("timerAlreadyExists");
2130 0 : break;
2131 :
2132 : case eTimerDoesntExist:
2133 0 : error.mError.AssignLiteral("timerDoesntExist");
2134 0 : break;
2135 :
2136 : case eTimerJSException:
2137 0 : error.mError.AssignLiteral("timerJSError");
2138 0 : break;
2139 :
2140 : case eTimerMaxReached:
2141 0 : error.mError.AssignLiteral("maxTimersExceeded");
2142 0 : break;
2143 :
2144 : default:
2145 0 : MOZ_CRASH("Unsupported status");
2146 : break;
2147 : }
2148 :
2149 0 : JS::Rooted<JS::Value> value(aCx);
2150 0 : if (!ToJSValue(aCx, error, &value)) {
2151 0 : return JS::UndefinedValue();
2152 : }
2153 :
2154 0 : return value;
2155 : }
2156 :
2157 : bool
2158 1 : Console::ArgumentsToValueList(const Sequence<JS::Value>& aData,
2159 : Sequence<JS::Value>& aSequence) const
2160 : {
2161 2 : for (uint32_t i = 0; i < aData.Length(); ++i) {
2162 1 : if (NS_WARN_IF(!aSequence.AppendElement(aData[i], fallible))) {
2163 0 : return false;
2164 : }
2165 : }
2166 :
2167 1 : return true;
2168 : }
2169 :
2170 : uint32_t
2171 0 : Console::IncreaseCounter(JSContext* aCx, const Sequence<JS::Value>& aArguments,
2172 : nsAString& aCountLabel)
2173 : {
2174 0 : AssertIsOnOwningThread();
2175 :
2176 0 : ClearException ce(aCx);
2177 :
2178 0 : MOZ_ASSERT(!aArguments.IsEmpty());
2179 :
2180 0 : JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
2181 0 : JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, labelValue));
2182 0 : if (!jsString) {
2183 0 : return 0; // We cannot continue.
2184 : }
2185 :
2186 0 : nsAutoJSString string;
2187 0 : if (!string.init(aCx, jsString)) {
2188 0 : return 0; // We cannot continue.
2189 : }
2190 :
2191 0 : aCountLabel = string;
2192 :
2193 0 : const bool maxCountersReached = mCounterRegistry.Count() >= MAX_PAGE_COUNTERS;
2194 0 : auto entry = mCounterRegistry.LookupForAdd(aCountLabel);
2195 0 : if (entry) {
2196 0 : ++entry.Data();
2197 : } else {
2198 0 : entry.OrInsert([](){ return 1; });
2199 0 : if (maxCountersReached) {
2200 : // oops, we speculatively added an entry even though we shouldn't
2201 0 : mCounterRegistry.Remove(aCountLabel);
2202 0 : return MAX_PAGE_COUNTERS;
2203 : }
2204 : }
2205 0 : return entry.Data();
2206 : }
2207 :
2208 : JS::Value
2209 0 : Console::CreateCounterValue(JSContext* aCx, const nsAString& aCountLabel,
2210 : uint32_t aCountValue) const
2211 : {
2212 0 : ClearException ce(aCx);
2213 :
2214 0 : if (aCountValue == MAX_PAGE_COUNTERS) {
2215 0 : RootedDictionary<ConsoleCounterError> error(aCx);
2216 :
2217 0 : JS::Rooted<JS::Value> value(aCx);
2218 0 : if (!ToJSValue(aCx, error, &value)) {
2219 0 : return JS::UndefinedValue();
2220 : }
2221 :
2222 0 : return value;
2223 : }
2224 :
2225 0 : RootedDictionary<ConsoleCounter> data(aCx);
2226 0 : data.mLabel = aCountLabel;
2227 0 : data.mCount = aCountValue;
2228 :
2229 0 : JS::Rooted<JS::Value> value(aCx);
2230 0 : if (!ToJSValue(aCx, data, &value)) {
2231 0 : return JS::UndefinedValue();
2232 : }
2233 :
2234 0 : return value;
2235 : }
2236 :
2237 : bool
2238 2 : Console::ShouldIncludeStackTrace(MethodName aMethodName) const
2239 : {
2240 2 : switch (aMethodName) {
2241 : case MethodError:
2242 : case MethodException:
2243 : case MethodAssert:
2244 : case MethodTrace:
2245 2 : return true;
2246 : default:
2247 0 : return false;
2248 : }
2249 : }
2250 :
2251 : JSObject*
2252 0 : Console::GetOrCreateSandbox(JSContext* aCx, nsIPrincipal* aPrincipal)
2253 : {
2254 0 : AssertIsOnMainThread();
2255 :
2256 0 : if (!mSandbox) {
2257 0 : nsIXPConnect* xpc = nsContentUtils::XPConnect();
2258 0 : MOZ_ASSERT(xpc, "This should never be null!");
2259 :
2260 0 : JS::Rooted<JSObject*> sandbox(aCx);
2261 0 : nsresult rv = xpc->CreateSandbox(aCx, aPrincipal, sandbox.address());
2262 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2263 0 : return nullptr;
2264 : }
2265 :
2266 0 : mSandbox = new JSObjectHolder(aCx, sandbox);
2267 : }
2268 :
2269 0 : return mSandbox->GetJSObject();
2270 : }
2271 :
2272 : void
2273 1 : Console::StoreCallData(ConsoleCallData* aCallData)
2274 : {
2275 1 : AssertIsOnOwningThread();
2276 :
2277 1 : MOZ_ASSERT(aCallData);
2278 1 : MOZ_ASSERT(!mCallDataStorage.Contains(aCallData));
2279 1 : MOZ_ASSERT(!mCallDataStoragePending.Contains(aCallData));
2280 :
2281 1 : mCallDataStorage.AppendElement(aCallData);
2282 :
2283 1 : if (mCallDataStorage.Length() > STORAGE_MAX_EVENTS) {
2284 0 : RefPtr<ConsoleCallData> callData = mCallDataStorage[0];
2285 0 : mCallDataStorage.RemoveElementAt(0);
2286 :
2287 0 : MOZ_ASSERT(callData->mStatus != ConsoleCallData::eToBeDeleted);
2288 :
2289 : // We cannot delete this object now because we have to trace its JSValues
2290 : // until the pending operation (ConsoleCallDataRunnable) is completed.
2291 0 : if (callData->mStatus == ConsoleCallData::eInUse) {
2292 0 : callData->mStatus = ConsoleCallData::eToBeDeleted;
2293 0 : mCallDataStoragePending.AppendElement(callData);
2294 : }
2295 : }
2296 1 : }
2297 :
2298 : void
2299 1 : Console::UnstoreCallData(ConsoleCallData* aCallData)
2300 : {
2301 1 : AssertIsOnOwningThread();
2302 :
2303 1 : MOZ_ASSERT(aCallData);
2304 :
2305 1 : MOZ_ASSERT(!mCallDataStoragePending.Contains(aCallData));
2306 :
2307 : // It can be that mCallDataStorage has been already cleaned in case the
2308 : // processing of the argument of some Console methods triggers the
2309 : // window.close().
2310 :
2311 1 : mCallDataStorage.RemoveElement(aCallData);
2312 1 : }
2313 :
2314 : void
2315 0 : Console::ReleaseCallData(ConsoleCallData* aCallData)
2316 : {
2317 0 : AssertIsOnOwningThread();
2318 0 : MOZ_ASSERT(aCallData);
2319 0 : MOZ_ASSERT(aCallData->mStatus == ConsoleCallData::eToBeDeleted);
2320 0 : MOZ_ASSERT(mCallDataStoragePending.Contains(aCallData));
2321 :
2322 0 : mCallDataStoragePending.RemoveElement(aCallData);
2323 0 : }
2324 :
2325 : void
2326 0 : Console::NotifyHandler(JSContext* aCx, const Sequence<JS::Value>& aArguments,
2327 : ConsoleCallData* aCallData)
2328 : {
2329 0 : AssertIsOnOwningThread();
2330 0 : MOZ_ASSERT(!NS_IsMainThread());
2331 0 : MOZ_ASSERT(aCallData);
2332 :
2333 0 : if (!mConsoleEventNotifier) {
2334 0 : return;
2335 : }
2336 :
2337 0 : JS::Rooted<JS::Value> value(aCx);
2338 :
2339 0 : JS::Rooted<JSObject*> callable(aCx, mConsoleEventNotifier->CallableOrNull());
2340 0 : if (NS_WARN_IF(!callable)) {
2341 0 : return;
2342 : }
2343 :
2344 : // aCx and aArguments are in the same compartment because this method is
2345 : // called directly when a Console.something() runs.
2346 : // mConsoleEventNotifier->Callable() is the scope where value will be sent to.
2347 0 : if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(aCx, aArguments,
2348 : callable,
2349 : &value,
2350 : aCallData))) {
2351 0 : return;
2352 : }
2353 :
2354 0 : JS::Rooted<JS::Value> ignored(aCx);
2355 0 : mConsoleEventNotifier->Call(value, &ignored);
2356 : }
2357 :
2358 : void
2359 0 : Console::RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents,
2360 : ErrorResult& aRv)
2361 : {
2362 0 : AssertIsOnOwningThread();
2363 :
2364 : // We don't want to expose this functionality to main-thread yet.
2365 0 : MOZ_ASSERT(!NS_IsMainThread());
2366 :
2367 0 : JS::Rooted<JSObject*> targetScope(aCx, JS::CurrentGlobalOrNull(aCx));
2368 :
2369 0 : for (uint32_t i = 0; i < mCallDataStorage.Length(); ++i) {
2370 0 : JS::Rooted<JS::Value> value(aCx);
2371 :
2372 0 : JS::Rooted<JSObject*> sequenceScope(aCx, mCallDataStorage[i]->mGlobal);
2373 0 : JSAutoCompartment ac(aCx, sequenceScope);
2374 :
2375 0 : Sequence<JS::Value> sequence;
2376 0 : SequenceRooter<JS::Value> arguments(aCx, &sequence);
2377 :
2378 0 : if (!mCallDataStorage[i]->PopulateArgumentsSequence(sequence)) {
2379 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2380 0 : return;
2381 : }
2382 :
2383 : // Here we have aCx and sequence in the same compartment.
2384 : // targetScope is the destination scope and value will be populated in its
2385 : // compartment.
2386 0 : if (NS_WARN_IF(!PopulateConsoleNotificationInTheTargetScope(aCx, sequence,
2387 : targetScope,
2388 : &value,
2389 : mCallDataStorage[i]))) {
2390 0 : aRv.Throw(NS_ERROR_FAILURE);
2391 0 : return;
2392 : }
2393 :
2394 0 : aEvents.AppendElement(value);
2395 : }
2396 : }
2397 :
2398 : void
2399 0 : Console::SetConsoleEventHandler(AnyCallback* aHandler)
2400 : {
2401 0 : AssertIsOnOwningThread();
2402 :
2403 : // We don't want to expose this functionality to main-thread yet.
2404 0 : MOZ_ASSERT(!NS_IsMainThread());
2405 :
2406 0 : mConsoleEventNotifier = aHandler;
2407 0 : }
2408 :
2409 : void
2410 5 : Console::AssertIsOnOwningThread() const
2411 : {
2412 5 : NS_ASSERT_OWNINGTHREAD(Console);
2413 5 : }
2414 :
2415 : bool
2416 1 : Console::IsShuttingDown() const
2417 : {
2418 1 : MOZ_ASSERT(mStatus != eUnknown);
2419 1 : return mStatus == eShuttingDown;
2420 : }
2421 :
2422 : /* static */ already_AddRefed<Console>
2423 1 : Console::GetConsole(const GlobalObject& aGlobal)
2424 : {
2425 2 : ErrorResult rv;
2426 2 : RefPtr<Console> console = GetConsoleInternal(aGlobal, rv);
2427 1 : if (NS_WARN_IF(rv.Failed()) || !console) {
2428 0 : rv.SuppressException();
2429 0 : return nullptr;
2430 : }
2431 :
2432 1 : console->AssertIsOnOwningThread();
2433 :
2434 1 : if (console->IsShuttingDown()) {
2435 0 : return nullptr;
2436 : }
2437 :
2438 1 : return console.forget();
2439 : }
2440 :
2441 : /* static */ Console*
2442 1 : Console::GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult& aRv)
2443 : {
2444 : // Worklet
2445 1 : if (NS_IsMainThread()) {
2446 : nsCOMPtr<WorkletGlobalScope> workletScope =
2447 2 : do_QueryInterface(aGlobal.GetAsSupports());
2448 1 : if (workletScope) {
2449 0 : return workletScope->GetConsole(aRv);
2450 : }
2451 : }
2452 :
2453 : // Window
2454 1 : if (NS_IsMainThread()) {
2455 : nsCOMPtr<nsPIDOMWindowInner> innerWindow =
2456 2 : do_QueryInterface(aGlobal.GetAsSupports());
2457 1 : if (NS_WARN_IF(!innerWindow)) {
2458 0 : return nullptr;
2459 : }
2460 :
2461 1 : nsGlobalWindow* window = nsGlobalWindow::Cast(innerWindow);
2462 1 : return window->GetConsole(aRv);
2463 : }
2464 :
2465 : // Workers
2466 0 : MOZ_ASSERT(!NS_IsMainThread());
2467 :
2468 0 : JSContext* cx = aGlobal.Context();
2469 0 : WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
2470 0 : MOZ_ASSERT(workerPrivate);
2471 :
2472 : nsCOMPtr<nsIGlobalObject> global =
2473 0 : do_QueryInterface(aGlobal.GetAsSupports());
2474 0 : if (NS_WARN_IF(!global)) {
2475 0 : return nullptr;
2476 : }
2477 :
2478 0 : WorkerGlobalScope* scope = workerPrivate->GlobalScope();
2479 0 : MOZ_ASSERT(scope);
2480 :
2481 : // Normal worker scope.
2482 0 : if (scope == global) {
2483 0 : return scope->GetConsole(aRv);
2484 : }
2485 :
2486 : // Debugger worker scope
2487 : else {
2488 : WorkerDebuggerGlobalScope* debuggerScope =
2489 0 : workerPrivate->DebuggerGlobalScope();
2490 0 : MOZ_ASSERT(debuggerScope);
2491 0 : MOZ_ASSERT(debuggerScope == global, "Which kind of global do we have?");
2492 :
2493 0 : return debuggerScope->GetConsole(aRv);
2494 : }
2495 : }
2496 :
2497 : } // namespace dom
2498 : } // namespace mozilla
|