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 "ConsoleUtils.h"
8 #include "ConsoleCommon.h"
9 #include "nsContentUtils.h"
10 #include "nsIConsoleAPIStorage.h"
11 #include "nsIXPConnect.h"
12 #include "nsServiceManagerUtils.h"
13 
14 #include "mozilla/ClearOnShutdown.h"
15 #include "mozilla/NullPrincipal.h"
16 #include "mozilla/dom/ConsoleBinding.h"
17 #include "mozilla/dom/RootedDictionary.h"
18 #include "mozilla/dom/ScriptSettings.h"
19 
20 namespace mozilla::dom {
21 
22 namespace {
23 
24 StaticRefPtr<ConsoleUtils> gConsoleUtilsService;
25 
26 }
27 
28 /* static */
GetOrCreate()29 ConsoleUtils* ConsoleUtils::GetOrCreate() {
30   if (!gConsoleUtilsService) {
31     MOZ_ASSERT(NS_IsMainThread());
32 
33     gConsoleUtilsService = new ConsoleUtils();
34     ClearOnShutdown(&gConsoleUtilsService);
35   }
36 
37   return gConsoleUtilsService;
38 }
39 
40 ConsoleUtils::ConsoleUtils() = default;
41 ConsoleUtils::~ConsoleUtils() = default;
42 
43 /* static */
ReportForServiceWorkerScope(const nsAString & aScope,const nsAString & aMessage,const nsAString & aFilename,uint32_t aLineNumber,uint32_t aColumnNumber,Level aLevel)44 void ConsoleUtils::ReportForServiceWorkerScope(const nsAString& aScope,
45                                                const nsAString& aMessage,
46                                                const nsAString& aFilename,
47                                                uint32_t aLineNumber,
48                                                uint32_t aColumnNumber,
49                                                Level aLevel) {
50   MOZ_ASSERT(NS_IsMainThread());
51 
52   RefPtr<ConsoleUtils> service = ConsoleUtils::GetOrCreate();
53   if (NS_WARN_IF(!service)) {
54     return;
55   }
56 
57   service->ReportForServiceWorkerScopeInternal(
58       aScope, aMessage, aFilename, aLineNumber, aColumnNumber, aLevel);
59 }
60 
ReportForServiceWorkerScopeInternal(const nsAString & aScope,const nsAString & aMessage,const nsAString & aFilename,uint32_t aLineNumber,uint32_t aColumnNumber,Level aLevel)61 void ConsoleUtils::ReportForServiceWorkerScopeInternal(
62     const nsAString& aScope, const nsAString& aMessage,
63     const nsAString& aFilename, uint32_t aLineNumber, uint32_t aColumnNumber,
64     Level aLevel) {
65   MOZ_ASSERT(NS_IsMainThread());
66 
67   AutoJSAPI jsapi;
68   jsapi.Init();
69 
70   JSContext* cx = jsapi.cx();
71 
72   ConsoleCommon::ClearException ce(cx);
73   JS::Rooted<JSObject*> global(cx, GetOrCreateSandbox(cx));
74   if (NS_WARN_IF(!global)) {
75     return;
76   }
77 
78   // The GetOrCreateSandbox call returns a proxy to the actual sandbox object.
79   // We don't need a proxy here.
80   global = js::UncheckedUnwrap(global);
81 
82   JSAutoRealm ar(cx, global);
83 
84   RootedDictionary<ConsoleEvent> event(cx);
85 
86   event.mID.Construct();
87   event.mID.Value().SetAsString() = aScope;
88 
89   event.mInnerID.Construct();
90   event.mInnerID.Value().SetAsString() = u"ServiceWorker"_ns;
91 
92   switch (aLevel) {
93     case eLog:
94       event.mLevel = u"log"_ns;
95       break;
96 
97     case eWarning:
98       event.mLevel = u"warn"_ns;
99       break;
100 
101     case eError:
102       event.mLevel = u"error"_ns;
103       break;
104   }
105 
106   event.mFilename = aFilename;
107   event.mLineNumber = aLineNumber;
108   event.mColumnNumber = aColumnNumber;
109   event.mTimeStamp = JS_Now() / PR_USEC_PER_MSEC;
110 
111   JS::Rooted<JS::Value> messageValue(cx);
112   if (!dom::ToJSValue(cx, aMessage, &messageValue)) {
113     return;
114   }
115 
116   event.mArguments.Construct();
117   if (!event.mArguments.Value().AppendElement(messageValue, fallible)) {
118     return;
119   }
120 
121   nsCOMPtr<nsIConsoleAPIStorage> storage =
122       do_GetService("@mozilla.org/consoleAPI-storage;1");
123 
124   if (NS_WARN_IF(!storage)) {
125     return;
126   }
127 
128   JS::Rooted<JS::Value> eventValue(cx);
129   if (!ToJSValue(cx, event, &eventValue)) {
130     return;
131   }
132 
133   // This is a legacy property.
134   JS::Rooted<JSObject*> eventObj(cx, &eventValue.toObject());
135   if (NS_WARN_IF(!JS_DefineProperty(cx, eventObj, "wrappedJSObject", eventObj,
136                                     JSPROP_ENUMERATE))) {
137     return;
138   }
139 
140   storage->RecordEvent(u"ServiceWorker"_ns, aScope, eventValue);
141 }
142 
GetOrCreateSandbox(JSContext * aCx)143 JSObject* ConsoleUtils::GetOrCreateSandbox(JSContext* aCx) {
144   AssertIsOnMainThread();
145 
146   if (!mSandbox) {
147     nsIXPConnect* xpc = nsContentUtils::XPConnect();
148     MOZ_ASSERT(xpc, "This should never be null!");
149 
150     RefPtr<NullPrincipal> nullPrincipal =
151         NullPrincipal::CreateWithoutOriginAttributes();
152 
153     JS::Rooted<JSObject*> sandbox(aCx);
154     nsresult rv = xpc->CreateSandbox(aCx, nullPrincipal, sandbox.address());
155     if (NS_WARN_IF(NS_FAILED(rv))) {
156       return nullptr;
157     }
158 
159     mSandbox = new JSObjectHolder(aCx, sandbox);
160   }
161 
162   return mSandbox->GetJSObject();
163 }
164 
165 }  // namespace mozilla::dom
166