PageRenderTime 27ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/firefox-14.0.1/mozilla-release/xpcom/base/nsConsoleService.cpp

#
C++ | 338 lines | 202 code | 57 blank | 79 comment | 30 complexity | 1ebdafc9ace31c2682925d40e50462ee MD5 | raw file
Possible License(s): MIT, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause, GPL-2.0, JSON, Apache-2.0, 0BSD, LGPL-3.0, AGPL-1.0
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * The Original Code is mozilla.org code.
  16. *
  17. * The Initial Developer of the Original Code is
  18. * Netscape Communications Corporation.
  19. * Portions created by the Initial Developer are Copyright (C) 1998
  20. * the Initial Developer. All Rights Reserved.
  21. *
  22. * Contributor(s):
  23. * Simon B?nzli <zeniko@gmail.com>
  24. *
  25. * Alternatively, the contents of this file may be used under the terms of
  26. * either of the GNU General Public License Version 2 or later (the "GPL"),
  27. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28. * in which case the provisions of the GPL or the LGPL are applicable instead
  29. * of those above. If you wish to allow use of your version of this file only
  30. * under the terms of either the GPL or the LGPL, and not to allow others to
  31. * use your version of this file under the terms of the MPL, indicate your
  32. * decision by deleting the provisions above and replace them with the notice
  33. * and other provisions required by the GPL or the LGPL. If you do not delete
  34. * the provisions above, a recipient may use your version of this file under
  35. * the terms of any one of the MPL, the GPL or the LGPL.
  36. *
  37. * ***** END LICENSE BLOCK ***** */
  38. /*
  39. * Maintains a circular buffer of recent messages, and notifies
  40. * listeners when new messages are logged.
  41. */
  42. /* Threadsafe. */
  43. #include "nsMemory.h"
  44. #include "nsIServiceManager.h"
  45. #include "nsCOMArray.h"
  46. #include "nsThreadUtils.h"
  47. #include "nsConsoleService.h"
  48. #include "nsConsoleMessage.h"
  49. #include "nsIClassInfoImpl.h"
  50. #if defined(ANDROID)
  51. #include <android/log.h>
  52. #endif
  53. using namespace mozilla;
  54. NS_IMPL_THREADSAFE_ADDREF(nsConsoleService)
  55. NS_IMPL_THREADSAFE_RELEASE(nsConsoleService)
  56. NS_IMPL_CLASSINFO(nsConsoleService, NULL, nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON, NS_CONSOLESERVICE_CID)
  57. NS_IMPL_QUERY_INTERFACE1_CI(nsConsoleService, nsIConsoleService)
  58. NS_IMPL_CI_INTERFACE_GETTER1(nsConsoleService, nsIConsoleService)
  59. nsConsoleService::nsConsoleService()
  60. : mMessages(nsnull)
  61. , mCurrent(0)
  62. , mFull(false)
  63. , mDeliveringMessage(false)
  64. , mLock("nsConsoleService.mLock")
  65. {
  66. // XXX grab this from a pref!
  67. // hm, but worry about circularity, bc we want to be able to report
  68. // prefs errs...
  69. mBufferSize = 250;
  70. }
  71. nsConsoleService::~nsConsoleService()
  72. {
  73. PRUint32 i = 0;
  74. while (i < mBufferSize && mMessages[i] != nsnull) {
  75. NS_RELEASE(mMessages[i]);
  76. i++;
  77. }
  78. if (mMessages)
  79. nsMemory::Free(mMessages);
  80. }
  81. nsresult
  82. nsConsoleService::Init()
  83. {
  84. mMessages = (nsIConsoleMessage **)
  85. nsMemory::Alloc(mBufferSize * sizeof(nsIConsoleMessage *));
  86. if (!mMessages)
  87. return NS_ERROR_OUT_OF_MEMORY;
  88. // Array elements should be 0 initially for circular buffer algorithm.
  89. memset(mMessages, 0, mBufferSize * sizeof(nsIConsoleMessage *));
  90. mListeners.Init();
  91. return NS_OK;
  92. }
  93. namespace {
  94. class LogMessageRunnable : public nsRunnable
  95. {
  96. public:
  97. LogMessageRunnable(nsIConsoleMessage* message, nsConsoleService* service)
  98. : mMessage(message)
  99. , mService(service)
  100. { }
  101. void AddListener(nsIConsoleListener* listener) {
  102. mListeners.AppendObject(listener);
  103. }
  104. NS_DECL_NSIRUNNABLE
  105. private:
  106. nsCOMPtr<nsIConsoleMessage> mMessage;
  107. nsRefPtr<nsConsoleService> mService;
  108. nsCOMArray<nsIConsoleListener> mListeners;
  109. };
  110. NS_IMETHODIMP
  111. LogMessageRunnable::Run()
  112. {
  113. MOZ_ASSERT(NS_IsMainThread());
  114. mService->SetIsDelivering();
  115. for (PRInt32 i = 0; i < mListeners.Count(); ++i)
  116. mListeners[i]->Observe(mMessage);
  117. mService->SetDoneDelivering();
  118. return NS_OK;
  119. }
  120. PLDHashOperator
  121. CollectCurrentListeners(nsISupports* aKey, nsIConsoleListener* aValue,
  122. void* closure)
  123. {
  124. LogMessageRunnable* r = static_cast<LogMessageRunnable*>(closure);
  125. r->AddListener(aValue);
  126. return PL_DHASH_NEXT;
  127. }
  128. } // anonymous namespace
  129. // nsIConsoleService methods
  130. NS_IMETHODIMP
  131. nsConsoleService::LogMessage(nsIConsoleMessage *message)
  132. {
  133. if (message == nsnull)
  134. return NS_ERROR_INVALID_ARG;
  135. if (NS_IsMainThread() && mDeliveringMessage) {
  136. NS_WARNING("Some console listener threw an error while inside itself. Discarding this message");
  137. return NS_ERROR_FAILURE;
  138. }
  139. nsRefPtr<LogMessageRunnable> r = new LogMessageRunnable(message, this);
  140. nsIConsoleMessage *retiredMessage;
  141. NS_ADDREF(message); // early, in case it's same as replaced below.
  142. /*
  143. * Lock while updating buffer, and while taking snapshot of
  144. * listeners array.
  145. */
  146. {
  147. MutexAutoLock lock(mLock);
  148. #if defined(ANDROID)
  149. {
  150. nsXPIDLString msg;
  151. message->GetMessageMoz(getter_Copies(msg));
  152. __android_log_print(ANDROID_LOG_ERROR, "GeckoConsole",
  153. "%s",
  154. NS_LossyConvertUTF16toASCII(msg).get());
  155. }
  156. #endif
  157. /*
  158. * If there's already a message in the slot we're about to replace,
  159. * we've wrapped around, and we need to release the old message. We
  160. * save a pointer to it, so we can release below outside the lock.
  161. */
  162. retiredMessage = mMessages[mCurrent];
  163. mMessages[mCurrent++] = message;
  164. if (mCurrent == mBufferSize) {
  165. mCurrent = 0; // wrap around.
  166. mFull = true;
  167. }
  168. /*
  169. * Copy the listeners into the snapshot array - in case a listener
  170. * is removed during an Observe(...) notification...
  171. */
  172. mListeners.EnumerateRead(CollectCurrentListeners, r);
  173. }
  174. if (retiredMessage != nsnull)
  175. NS_RELEASE(retiredMessage);
  176. NS_DispatchToMainThread(r);
  177. return NS_OK;
  178. }
  179. NS_IMETHODIMP
  180. nsConsoleService::LogStringMessage(const PRUnichar *message)
  181. {
  182. nsConsoleMessage *msg = new nsConsoleMessage(message);
  183. return this->LogMessage(msg);
  184. }
  185. NS_IMETHODIMP
  186. nsConsoleService::GetMessageArray(nsIConsoleMessage ***messages, PRUint32 *count)
  187. {
  188. nsIConsoleMessage **messageArray;
  189. /*
  190. * Lock the whole method, as we don't want anyone mucking with mCurrent or
  191. * mFull while we're copying out the buffer.
  192. */
  193. MutexAutoLock lock(mLock);
  194. if (mCurrent == 0 && !mFull) {
  195. /*
  196. * Make a 1-length output array so that nobody gets confused,
  197. * and return a count of 0. This should result in a 0-length
  198. * array object when called from script.
  199. */
  200. messageArray = (nsIConsoleMessage **)
  201. nsMemory::Alloc(sizeof (nsIConsoleMessage *));
  202. *messageArray = nsnull;
  203. *messages = messageArray;
  204. *count = 0;
  205. return NS_OK;
  206. }
  207. PRUint32 resultSize = mFull ? mBufferSize : mCurrent;
  208. messageArray =
  209. (nsIConsoleMessage **)nsMemory::Alloc((sizeof (nsIConsoleMessage *))
  210. * resultSize);
  211. if (messageArray == nsnull) {
  212. *messages = nsnull;
  213. *count = 0;
  214. return NS_ERROR_FAILURE;
  215. }
  216. PRUint32 i;
  217. if (mFull) {
  218. for (i = 0; i < mBufferSize; i++) {
  219. // if full, fill the buffer starting from mCurrent (which'll be
  220. // oldest) wrapping around the buffer to the most recent.
  221. messageArray[i] = mMessages[(mCurrent + i) % mBufferSize];
  222. NS_ADDREF(messageArray[i]);
  223. }
  224. } else {
  225. for (i = 0; i < mCurrent; i++) {
  226. messageArray[i] = mMessages[i];
  227. NS_ADDREF(messageArray[i]);
  228. }
  229. }
  230. *count = resultSize;
  231. *messages = messageArray;
  232. return NS_OK;
  233. }
  234. NS_IMETHODIMP
  235. nsConsoleService::RegisterListener(nsIConsoleListener *listener)
  236. {
  237. if (!NS_IsMainThread()) {
  238. NS_ERROR("nsConsoleService::RegisterListener is main thread only.");
  239. return NS_ERROR_NOT_SAME_THREAD;
  240. }
  241. nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
  242. MutexAutoLock lock(mLock);
  243. if (mListeners.GetWeak(canonical)) {
  244. // Reregistering a listener isn't good
  245. return NS_ERROR_FAILURE;
  246. }
  247. mListeners.Put(canonical, listener);
  248. return NS_OK;
  249. }
  250. NS_IMETHODIMP
  251. nsConsoleService::UnregisterListener(nsIConsoleListener *listener)
  252. {
  253. if (!NS_IsMainThread()) {
  254. NS_ERROR("nsConsoleService::UnregisterListener is main thread only.");
  255. return NS_ERROR_NOT_SAME_THREAD;
  256. }
  257. nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
  258. MutexAutoLock lock(mLock);
  259. if (!mListeners.GetWeak(canonical)) {
  260. // Unregistering a listener that was never registered?
  261. return NS_ERROR_FAILURE;
  262. }
  263. mListeners.Remove(canonical);
  264. return NS_OK;
  265. }
  266. NS_IMETHODIMP
  267. nsConsoleService::Reset()
  268. {
  269. /*
  270. * Make sure nobody trips into the buffer while it's being reset
  271. */
  272. MutexAutoLock lock(mLock);
  273. mCurrent = 0;
  274. mFull = false;
  275. /*
  276. * Free all messages stored so far (cf. destructor)
  277. */
  278. for (PRUint32 i = 0; i < mBufferSize && mMessages[i] != nsnull; i++)
  279. NS_RELEASE(mMessages[i]);
  280. return NS_OK;
  281. }