xpcom/base/nsConsoleService.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /*
     7  * Maintains a circular buffer of recent messages, and notifies
     8  * listeners when new messages are logged.
     9  */
    11 /* Threadsafe. */
    13 #include "nsMemory.h"
    14 #include "nsCOMArray.h"
    15 #include "nsThreadUtils.h"
    17 #include "nsConsoleService.h"
    18 #include "nsConsoleMessage.h"
    19 #include "nsIClassInfoImpl.h"
    20 #include "nsIConsoleListener.h"
    21 #include "nsPrintfCString.h"
    23 #include "mozilla/Preferences.h"
    25 #if defined(ANDROID)
    26 #include <android/log.h>
    27 #endif
    28 #ifdef XP_WIN
    29 #include <windows.h>
    30 #endif
    32 using namespace mozilla;
    34 NS_IMPL_ADDREF(nsConsoleService)
    35 NS_IMPL_RELEASE(nsConsoleService)
    36 NS_IMPL_CLASSINFO(nsConsoleService, nullptr, nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON, NS_CONSOLESERVICE_CID)
    37 NS_IMPL_QUERY_INTERFACE_CI(nsConsoleService, nsIConsoleService)
    38 NS_IMPL_CI_INTERFACE_GETTER(nsConsoleService, nsIConsoleService)
    40 static bool sLoggingEnabled = true;
    41 static bool sLoggingBuffered = true;
    43 nsConsoleService::nsConsoleService()
    44     : mMessages(nullptr)
    45     , mCurrent(0)
    46     , mFull(false)
    47     , mDeliveringMessage(false)
    48     , mLock("nsConsoleService.mLock")
    49 {
    50     // XXX grab this from a pref!
    51     // hm, but worry about circularity, bc we want to be able to report
    52     // prefs errs...
    53     mBufferSize = 250;
    54 }
    56 nsConsoleService::~nsConsoleService()
    57 {
    58     uint32_t i = 0;
    59     while (i < mBufferSize && mMessages[i] != nullptr) {
    60         NS_RELEASE(mMessages[i]);
    61         i++;
    62     }
    64     if (mMessages)
    65         nsMemory::Free(mMessages);
    66 }
    68 class AddConsolePrefWatchers : public nsRunnable
    69 {
    70 public:
    71     AddConsolePrefWatchers(nsConsoleService* aConsole) : mConsole(aConsole) {}
    73     NS_IMETHOD Run()
    74     {
    75         Preferences::AddBoolVarCache(&sLoggingEnabled, "consoleservice.enabled", true);
    76         Preferences::AddBoolVarCache(&sLoggingBuffered, "consoleservice.buffered", true);
    77         if (!sLoggingBuffered) {
    78             mConsole->Reset();
    79         }
    80         return NS_OK;
    81     }
    83 private:
    84     nsRefPtr<nsConsoleService> mConsole;
    85 };
    87 nsresult
    88 nsConsoleService::Init()
    89 {
    90     mMessages = (nsIConsoleMessage **)
    91         nsMemory::Alloc(mBufferSize * sizeof(nsIConsoleMessage *));
    92     if (!mMessages)
    93         return NS_ERROR_OUT_OF_MEMORY;
    95     // Array elements should be 0 initially for circular buffer algorithm.
    96     memset(mMessages, 0, mBufferSize * sizeof(nsIConsoleMessage *));
    98     NS_DispatchToMainThread(new AddConsolePrefWatchers(this));
   100     return NS_OK;
   101 }
   103 namespace {
   105 class LogMessageRunnable : public nsRunnable
   106 {
   107 public:
   108     LogMessageRunnable(nsIConsoleMessage* message, nsConsoleService* service)
   109         : mMessage(message)
   110         , mService(service)
   111     { }
   113     NS_DECL_NSIRUNNABLE
   115 private:
   116     nsCOMPtr<nsIConsoleMessage> mMessage;
   117     nsRefPtr<nsConsoleService> mService;
   118 };
   120 typedef nsCOMArray<nsIConsoleListener> ListenerArrayType;
   122 PLDHashOperator
   123 CollectCurrentListeners(nsISupports* aKey, nsIConsoleListener* aValue,
   124                         void* closure)
   125 {
   126     ListenerArrayType* listeners = static_cast<ListenerArrayType*>(closure);
   127     listeners->AppendObject(aValue);
   128     return PL_DHASH_NEXT;
   129 }
   131 NS_IMETHODIMP
   132 LogMessageRunnable::Run()
   133 {
   134     MOZ_ASSERT(NS_IsMainThread());
   136     // Snapshot of listeners so that we don't reenter this hash during
   137     // enumeration.
   138     nsCOMArray<nsIConsoleListener> listeners;
   139     mService->EnumerateListeners(CollectCurrentListeners, &listeners);
   141     mService->SetIsDelivering();
   143     for (int32_t i = 0; i < listeners.Count(); ++i)
   144         listeners[i]->Observe(mMessage);
   146     mService->SetDoneDelivering();
   148     return NS_OK;
   149 }
   151 } // anonymous namespace
   153 // nsIConsoleService methods
   154 NS_IMETHODIMP
   155 nsConsoleService::LogMessage(nsIConsoleMessage *message)
   156 {
   157     return LogMessageWithMode(message, OutputToLog);
   158 }
   160 nsresult
   161 nsConsoleService::LogMessageWithMode(nsIConsoleMessage *message, nsConsoleService::OutputMode outputMode)
   162 {
   163     if (message == nullptr)
   164         return NS_ERROR_INVALID_ARG;
   166     if (!sLoggingEnabled) {
   167         return NS_OK;
   168     }
   170     if (NS_IsMainThread() && mDeliveringMessage) {
   171         nsCString msg;
   172         message->ToString(msg);
   173         NS_WARNING(nsPrintfCString("Reentrancy error: some client attempted "
   174             "to display a message to the console while in a console listener. "
   175             "The following message was discarded: \"%s\"", msg.get()).get());
   176         return NS_ERROR_FAILURE;
   177     }
   179     nsRefPtr<LogMessageRunnable> r;
   180     nsIConsoleMessage *retiredMessage;
   182     if (sLoggingBuffered) {
   183         NS_ADDREF(message); // early, in case it's same as replaced below.
   184     }
   186     /*
   187      * Lock while updating buffer, and while taking snapshot of
   188      * listeners array.
   189      */
   190     {
   191         MutexAutoLock lock(mLock);
   193 #if defined(ANDROID)
   194         if (outputMode == OutputToLog)
   195         {
   196             nsCString msg;
   197             message->ToString(msg);
   198             __android_log_print(ANDROID_LOG_ERROR, "GeckoConsole",
   199                         "%s", msg.get());
   200         }
   201 #endif
   202 #ifdef XP_WIN
   203         if (IsDebuggerPresent()) {
   204             nsString msg;
   205             message->GetMessageMoz(getter_Copies(msg));
   206             msg.AppendLiteral("\n");
   207             OutputDebugStringW(msg.get());
   208         }
   209 #endif
   211         /*
   212          * If there's already a message in the slot we're about to replace,
   213          * we've wrapped around, and we need to release the old message.  We
   214          * save a pointer to it, so we can release below outside the lock.
   215          */
   216         retiredMessage = mMessages[mCurrent];
   218         if (sLoggingBuffered) {
   219             mMessages[mCurrent++] = message;
   220             if (mCurrent == mBufferSize) {
   221                 mCurrent = 0; // wrap around.
   222                 mFull = true;
   223             }
   224         }
   226         if (mListeners.Count() > 0) {
   227             r = new LogMessageRunnable(message, this);
   228         }
   229     }
   231     if (retiredMessage != nullptr)
   232         NS_RELEASE(retiredMessage);
   234     if (r)
   235         NS_DispatchToMainThread(r);
   237     return NS_OK;
   238 }
   240 void
   241 nsConsoleService::EnumerateListeners(ListenerHash::EnumReadFunction aFunction,
   242                                      void* aClosure)
   243 {
   244     MutexAutoLock lock(mLock);
   245     mListeners.EnumerateRead(aFunction, aClosure);
   246 }
   248 NS_IMETHODIMP
   249 nsConsoleService::LogStringMessage(const char16_t *message)
   250 {
   251     if (!sLoggingEnabled) {
   252         return NS_OK;
   253     }
   255     nsRefPtr<nsConsoleMessage> msg(new nsConsoleMessage(message));
   256     return this->LogMessage(msg);
   257 }
   259 NS_IMETHODIMP
   260 nsConsoleService::GetMessageArray(uint32_t *count, nsIConsoleMessage ***messages)
   261 {
   262     nsIConsoleMessage **messageArray;
   264     /*
   265      * Lock the whole method, as we don't want anyone mucking with mCurrent or
   266      * mFull while we're copying out the buffer.
   267      */
   268     MutexAutoLock lock(mLock);
   270     if (mCurrent == 0 && !mFull) {
   271         /*
   272          * Make a 1-length output array so that nobody gets confused,
   273          * and return a count of 0.  This should result in a 0-length
   274          * array object when called from script.
   275          */
   276         messageArray = (nsIConsoleMessage **)
   277             nsMemory::Alloc(sizeof (nsIConsoleMessage *));
   278         *messageArray = nullptr;
   279         *messages = messageArray;
   280         *count = 0;
   282         return NS_OK;
   283     }
   285     uint32_t resultSize = mFull ? mBufferSize : mCurrent;
   286     messageArray =
   287         (nsIConsoleMessage **)nsMemory::Alloc((sizeof (nsIConsoleMessage *))
   288                                               * resultSize);
   290     if (messageArray == nullptr) {
   291         *messages = nullptr;
   292         *count = 0;
   293         return NS_ERROR_FAILURE;
   294     }
   296     uint32_t i;
   297     if (mFull) {
   298         for (i = 0; i < mBufferSize; i++) {
   299             // if full, fill the buffer starting from mCurrent (which'll be
   300             // oldest) wrapping around the buffer to the most recent.
   301             messageArray[i] = mMessages[(mCurrent + i) % mBufferSize];
   302             NS_ADDREF(messageArray[i]);
   303         }
   304     } else {
   305         for (i = 0; i < mCurrent; i++) {
   306             messageArray[i] = mMessages[i];
   307             NS_ADDREF(messageArray[i]);
   308         }
   309     }
   310     *count = resultSize;
   311     *messages = messageArray;
   313     return NS_OK;
   314 }
   316 NS_IMETHODIMP
   317 nsConsoleService::RegisterListener(nsIConsoleListener *listener)
   318 {
   319     if (!NS_IsMainThread()) {
   320         NS_ERROR("nsConsoleService::RegisterListener is main thread only.");
   321         return NS_ERROR_NOT_SAME_THREAD;
   322     }
   324     nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
   326     MutexAutoLock lock(mLock);
   327     if (mListeners.GetWeak(canonical)) {
   328         // Reregistering a listener isn't good
   329         return NS_ERROR_FAILURE;
   330     }
   331     mListeners.Put(canonical, listener);
   332     return NS_OK;
   333 }
   335 NS_IMETHODIMP
   336 nsConsoleService::UnregisterListener(nsIConsoleListener *listener)
   337 {
   338     if (!NS_IsMainThread()) {
   339         NS_ERROR("nsConsoleService::UnregisterListener is main thread only.");
   340         return NS_ERROR_NOT_SAME_THREAD;
   341     }
   343     nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
   345     MutexAutoLock lock(mLock);
   347     if (!mListeners.GetWeak(canonical)) {
   348         // Unregistering a listener that was never registered?
   349         return NS_ERROR_FAILURE;
   350     }
   351     mListeners.Remove(canonical);
   352     return NS_OK;
   353 }
   355 NS_IMETHODIMP
   356 nsConsoleService::Reset()
   357 {
   358     /*
   359      * Make sure nobody trips into the buffer while it's being reset
   360      */
   361     MutexAutoLock lock(mLock);
   363     mCurrent = 0;
   364     mFull = false;
   366     /*
   367      * Free all messages stored so far (cf. destructor)
   368      */
   369     for (uint32_t i = 0; i < mBufferSize && mMessages[i] != nullptr; i++)
   370         NS_RELEASE(mMessages[i]);
   372     return NS_OK;
   373 }

mercurial