ConsoleInstance.cpp (9609B)
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/ConsoleInstance.h" 8 9 #include "Console.h" 10 #include "ConsoleCommon.h" 11 #include "ConsoleUtils.h" 12 #include "mozilla/Preferences.h" 13 #include "mozilla/dom/ConsoleBinding.h" 14 #include "nsContentUtils.h" 15 16 namespace mozilla::dom { 17 18 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ConsoleInstance, mConsole) 19 20 NS_IMPL_CYCLE_COLLECTING_ADDREF(ConsoleInstance) 21 NS_IMPL_CYCLE_COLLECTING_RELEASE(ConsoleInstance) 22 23 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ConsoleInstance) 24 NS_INTERFACE_MAP_ENTRY(nsISupports) 25 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 26 NS_INTERFACE_MAP_END 27 28 namespace { 29 30 ConsoleUtils::Level WebIDLevelToConsoleUtilsLevel(ConsoleLevel aLevel) { 31 switch (aLevel) { 32 case ConsoleLevel::Log: 33 return ConsoleUtils::eLog; 34 case ConsoleLevel::Warning: 35 return ConsoleUtils::eWarning; 36 case ConsoleLevel::Error: 37 return ConsoleUtils::eError; 38 default: 39 break; 40 } 41 42 return ConsoleUtils::eLog; 43 } 44 45 } // namespace 46 47 ConsoleInstance::ConsoleInstance(JSContext* aCx, 48 const ConsoleInstanceOptions& aOptions) 49 : mMaxLogLevel(ConsoleLogLevel::All), 50 mConsole(new Console(aCx, nullptr, 0, 0, aOptions.mPrefix)) { 51 mConsole->mConsoleID = aOptions.mConsoleID; 52 mConsole->mPassedInnerID = aOptions.mInnerID; 53 54 if (aOptions.mDump.WasPassed()) { 55 mConsole->mDumpFunction = &aOptions.mDump.Value(); 56 } 57 58 // Let's inform that this is a custom instance. 59 mConsole->mChromeInstance = true; 60 61 if (aOptions.mMaxLogLevel.WasPassed()) { 62 mMaxLogLevel = aOptions.mMaxLogLevel.Value(); 63 } 64 65 if (!aOptions.mMaxLogLevelPref.IsEmpty()) { 66 if (!NS_IsMainThread()) { 67 // Set the log level based on what we have. 68 SetLogLevel(); 69 70 // Flag an error to the console. 71 JS::Rooted<JS::Value> msg(aCx); 72 if (!ToJSValue( 73 aCx, 74 nsLiteralCString( 75 "Console.maxLogLevelPref is not supported within workers!"), 76 &msg)) { 77 JS_ClearPendingException(aCx); 78 return; 79 } 80 81 AutoTArray<JS::Value, 1> sequence; 82 SequenceRooter rootedSequence(aCx, &sequence); 83 sequence.AppendElement(std::move(msg)); 84 this->Error(aCx, std::move(sequence)); 85 return; 86 } 87 88 mMaxLogLevelPref = aOptions.mMaxLogLevelPref; 89 90 Preferences::RegisterCallback(MaxLogLevelPrefChangedCallback, 91 mMaxLogLevelPref, this); 92 } 93 SetLogLevel(); 94 } 95 96 ConsoleInstance::~ConsoleInstance() { 97 // We should only ever have set `mMaxLogLevelPref` when on the main thread, 98 // but check it here to be safe. 99 if (!mMaxLogLevelPref.IsEmpty() && NS_IsMainThread()) { 100 Preferences::UnregisterCallback(MaxLogLevelPrefChangedCallback, 101 mMaxLogLevelPref, this); 102 } 103 }; 104 105 ConsoleLogLevel PrefToValue(const nsACString& aPref, 106 const ConsoleLogLevel aLevel) { 107 if (aPref.IsEmpty()) { 108 return aLevel; 109 } 110 111 nsAutoCString value; 112 nsresult rv = Preferences::GetCString(PromiseFlatCString(aPref).get(), value); 113 if (NS_WARN_IF(NS_FAILED(rv))) { 114 nsString message; 115 message.AssignLiteral( 116 "Console.maxLogLevelPref used with a non-existing pref: "); 117 message.Append(NS_ConvertUTF8toUTF16(aPref)); 118 119 nsContentUtils::LogSimpleConsoleError(message, "chrome"_ns, false, 120 true /* from chrome context*/); 121 return aLevel; 122 } 123 124 Maybe<ConsoleLogLevel> level = StringToEnum<ConsoleLogLevel>(value); 125 if (NS_WARN_IF(level.isNothing())) { 126 nsString message; 127 message.AssignLiteral("Invalid Console.maxLogLevelPref value: "); 128 message.Append(NS_ConvertUTF8toUTF16(value)); 129 130 nsContentUtils::LogSimpleConsoleError(message, "chrome"_ns, false, 131 true /* from chrome context*/); 132 return aLevel; 133 } 134 135 return level.value(); 136 } 137 138 void ConsoleInstance::SetLogLevel() { 139 mConsole->mCurrentLogLevel = mConsole->WebIDLLogLevelToInteger( 140 PrefToValue(mMaxLogLevelPref, mMaxLogLevel)); 141 } 142 143 // static 144 void ConsoleInstance::MaxLogLevelPrefChangedCallback( 145 const char* /* aPrefName */, void* aSelf) { 146 auto* instance = static_cast<ConsoleInstance*>(aSelf); 147 if (MOZ_UNLIKELY(!instance->mConsole)) { 148 // We've been unlinked already but not destroyed yet. Bail. 149 return; 150 } 151 RefPtr pin{instance}; 152 pin->SetLogLevel(); 153 } 154 155 JSObject* ConsoleInstance::WrapObject(JSContext* aCx, 156 JS::Handle<JSObject*> aGivenProto) { 157 return ConsoleInstance_Binding::Wrap(aCx, this, aGivenProto); 158 } 159 160 #define METHOD(name, string) \ 161 void ConsoleInstance::name(JSContext* aCx, \ 162 const Sequence<JS::Value>& aData) { \ 163 RefPtr<Console> console(mConsole); \ 164 if (MOZ_UNLIKELY(!console)) { \ 165 return; \ 166 } \ 167 console->MethodInternal(aCx, Console::Method##name, \ 168 nsLiteralString(string), aData); \ 169 } 170 171 METHOD(Log, u"log") 172 METHOD(Info, u"info") 173 METHOD(Warn, u"warn") 174 METHOD(Error, u"error") 175 METHOD(Exception, u"exception") 176 METHOD(Debug, u"debug") 177 METHOD(Table, u"table") 178 METHOD(Trace, u"trace") 179 METHOD(Dir, u"dir"); 180 METHOD(Dirxml, u"dirxml"); 181 METHOD(Group, u"group") 182 METHOD(GroupCollapsed, u"groupCollapsed") 183 184 #undef METHOD 185 186 void ConsoleInstance::GroupEnd(JSContext* aCx) { 187 const Sequence<JS::Value> data; 188 RefPtr<Console> console(mConsole); 189 console->MethodInternal(aCx, Console::MethodGroupEnd, u"groupEnd"_ns, data); 190 } 191 192 void ConsoleInstance::Time(JSContext* aCx, const nsAString& aLabel) { 193 RefPtr<Console> console(mConsole); 194 console->StringMethodInternal(aCx, aLabel, Sequence<JS::Value>(), 195 Console::MethodTime, u"time"_ns); 196 } 197 198 void ConsoleInstance::TimeLog(JSContext* aCx, const nsAString& aLabel, 199 const Sequence<JS::Value>& aData) { 200 RefPtr<Console> console(mConsole); 201 console->StringMethodInternal(aCx, aLabel, aData, Console::MethodTimeLog, 202 u"timeLog"_ns); 203 } 204 205 void ConsoleInstance::TimeEnd(JSContext* aCx, const nsAString& aLabel) { 206 RefPtr<Console> console(mConsole); 207 console->StringMethodInternal(aCx, aLabel, Sequence<JS::Value>(), 208 Console::MethodTimeEnd, u"timeEnd"_ns); 209 } 210 211 void ConsoleInstance::TimeStamp(JSContext* aCx, 212 const JS::Handle<JS::Value> aData) { 213 ConsoleCommon::ClearException ce(aCx); 214 215 Sequence<JS::Value> data; 216 SequenceRooter<JS::Value> rooter(aCx, &data); 217 218 if (aData.isString() && !data.AppendElement(aData, fallible)) { 219 return; 220 } 221 222 RefPtr<Console> console(mConsole); 223 console->MethodInternal(aCx, Console::MethodTimeStamp, u"timeStamp"_ns, data); 224 } 225 226 void ConsoleInstance::Profile(JSContext* aCx, 227 const Sequence<JS::Value>& aData) { 228 RefPtr<Console> console(mConsole); 229 console->ProfileMethodInternal(aCx, Console::MethodProfile, u"profile"_ns, 230 aData); 231 } 232 233 void ConsoleInstance::ProfileEnd(JSContext* aCx, 234 const Sequence<JS::Value>& aData) { 235 RefPtr<Console> console(mConsole); 236 console->ProfileMethodInternal(aCx, Console::MethodProfileEnd, 237 u"profileEnd"_ns, aData); 238 } 239 240 void ConsoleInstance::Assert(JSContext* aCx, bool aCondition, 241 const Sequence<JS::Value>& aData) { 242 if (!aCondition) { 243 RefPtr<Console> console(mConsole); 244 console->MethodInternal(aCx, Console::MethodAssert, u"assert"_ns, aData); 245 } 246 } 247 248 void ConsoleInstance::Count(JSContext* aCx, const nsAString& aLabel) { 249 RefPtr<Console> console(mConsole); 250 console->StringMethodInternal(aCx, aLabel, Sequence<JS::Value>(), 251 Console::MethodCount, u"count"_ns); 252 } 253 254 void ConsoleInstance::CountReset(JSContext* aCx, const nsAString& aLabel) { 255 RefPtr<Console> console(mConsole); 256 console->StringMethodInternal(aCx, aLabel, Sequence<JS::Value>(), 257 Console::MethodCountReset, u"countReset"_ns); 258 } 259 260 void ConsoleInstance::Clear(JSContext* aCx) { 261 const Sequence<JS::Value> data; 262 RefPtr<Console> console(mConsole); 263 console->MethodInternal(aCx, Console::MethodClear, u"clear"_ns, data); 264 } 265 266 bool ConsoleInstance::ShouldLog(ConsoleLogLevel aLevel) { 267 return mConsole->mCurrentLogLevel <= 268 mConsole->WebIDLLogLevelToInteger(aLevel); 269 } 270 271 void ConsoleInstance::ReportForServiceWorkerScope(const nsAString& aScope, 272 const nsAString& aMessage, 273 const nsACString& aFilename, 274 uint32_t aLineNumber, 275 uint32_t aColumnNumber, 276 ConsoleLevel aLevel) { 277 if (!NS_IsMainThread()) { 278 return; 279 } 280 281 ConsoleUtils::ReportForServiceWorkerScope( 282 aScope, aMessage, aFilename, aLineNumber, aColumnNumber, 283 WebIDLevelToConsoleUtilsLevel(aLevel)); 284 } 285 286 } // namespace mozilla::dom