tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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