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
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/
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
15 * The Original Code is mozilla.org code.
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.
23 * Simon Bünzli <zeniko@gmail.com>
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.
37 * ***** END LICENSE BLOCK ***** */
40 * Maintains a circular buffer of recent messages, and notifies
41 * listeners when new messages are logged.
47 #include "nsIServiceManager.h"
48 #include "nsIProxyObjectManager.h"
49 #include "nsCOMArray.h"
50 #include "nsThreadUtils.h"
52 #include "nsConsoleService.h"
53 #include "nsConsoleMessage.h"
54 #include "nsIClassInfoImpl.h"
56 NS_IMPL_THREADSAFE_ADDREF(nsConsoleService
)
57 NS_IMPL_THREADSAFE_RELEASE(nsConsoleService
)
58 NS_IMPL_CLASSINFO(nsConsoleService
, NULL
, nsIClassInfo::THREADSAFE
| nsIClassInfo::SINGLETON
, NS_CONSOLESERVICE_CID
)
59 NS_IMPL_QUERY_INTERFACE1_CI(nsConsoleService
, nsIConsoleService
)
60 NS_IMPL_CI_INTERFACE_GETTER1(nsConsoleService
, nsIConsoleService
)
62 nsConsoleService::nsConsoleService()
63 : mMessages(nsnull
), mCurrent(0), mFull(PR_FALSE
), mListening(PR_FALSE
), mLock(nsnull
)
65 // XXX grab this from a pref!
66 // hm, but worry about circularity, bc we want to be able to report
71 nsConsoleService::~nsConsoleService()
74 while (i
< mBufferSize
&& mMessages
[i
] != nsnull
) {
75 NS_RELEASE(mMessages
[i
]);
80 if (mListeners
.Count() != 0) {
82 "WARNING - %d console error listeners still registered!\n"
83 "More calls to nsIConsoleService::UnregisterListener needed.\n",
90 nsMemory::Free(mMessages
);
92 PR_DestroyLock(mLock
);
96 nsConsoleService::Init()
98 mMessages
= (nsIConsoleMessage
**)
99 nsMemory::Alloc(mBufferSize
* sizeof(nsIConsoleMessage
*));
101 return NS_ERROR_OUT_OF_MEMORY
;
103 // Array elements should be 0 initially for circular buffer algorithm.
104 memset(mMessages
, 0, mBufferSize
* sizeof(nsIConsoleMessage
*));
106 mLock
= PR_NewLock();
108 return NS_ERROR_OUT_OF_MEMORY
;
113 static PRBool
snapshot_enum_func(nsHashKey
*key
, void *data
, void* closure
)
115 nsCOMArray
<nsIConsoleListener
> *array
=
116 reinterpret_cast<nsCOMArray
<nsIConsoleListener
> *>(closure
);
118 // Copy each element into the temporary nsCOMArray...
119 array
->AppendObject((nsIConsoleListener
*)data
);
123 // nsIConsoleService methods
125 nsConsoleService::LogMessage(nsIConsoleMessage
*message
)
127 if (message
== nsnull
)
128 return NS_ERROR_INVALID_ARG
;
130 nsCOMArray
<nsIConsoleListener
> listenersSnapshot
;
131 nsIConsoleMessage
*retiredMessage
;
133 NS_ADDREF(message
); // early, in case it's same as replaced below.
136 * Lock while updating buffer, and while taking snapshot of
140 nsAutoLock
lock(mLock
);
143 * If there's already a message in the slot we're about to replace,
144 * we've wrapped around, and we need to release the old message. We
145 * save a pointer to it, so we can release below outside the lock.
147 retiredMessage
= mMessages
[mCurrent
];
149 mMessages
[mCurrent
++] = message
;
150 if (mCurrent
== mBufferSize
) {
151 mCurrent
= 0; // wrap around.
156 * Copy the listeners into the snapshot array - in case a listener
157 * is removed during an Observe(...) notification...
159 mListeners
.Enumerate(snapshot_enum_func
, &listenersSnapshot
);
161 if (retiredMessage
!= nsnull
)
162 NS_RELEASE(retiredMessage
);
165 * Iterate through any registered listeners and tell them about
166 * the message. We use the mListening flag to guard against
167 * recursive message logs. This could sometimes result in
168 * listeners being skipped because of activity on other threads,
169 * when we only care about the recursive case.
171 nsCOMPtr
<nsIConsoleListener
> listener
;
172 PRInt32 snapshotCount
= listenersSnapshot
.Count();
175 nsAutoLock
lock(mLock
);
178 mListening
= PR_TRUE
;
181 for (PRInt32 i
= 0; i
< snapshotCount
; i
++) {
182 listenersSnapshot
[i
]->Observe(message
);
186 nsAutoLock
lock(mLock
);
187 mListening
= PR_FALSE
;
194 nsConsoleService::LogStringMessage(const PRUnichar
*message
)
196 nsConsoleMessage
*msg
= new nsConsoleMessage(message
);
197 return this->LogMessage(msg
);
201 nsConsoleService::GetMessageArray(nsIConsoleMessage
***messages
, PRUint32
*count
)
203 nsIConsoleMessage
**messageArray
;
206 * Lock the whole method, as we don't want anyone mucking with mCurrent or
207 * mFull while we're copying out the buffer.
209 nsAutoLock
lock(mLock
);
211 if (mCurrent
== 0 && !mFull
) {
213 * Make a 1-length output array so that nobody gets confused,
214 * and return a count of 0. This should result in a 0-length
215 * array object when called from script.
217 messageArray
= (nsIConsoleMessage
**)
218 nsMemory::Alloc(sizeof (nsIConsoleMessage
*));
219 *messageArray
= nsnull
;
220 *messages
= messageArray
;
226 PRUint32 resultSize
= mFull
? mBufferSize
: mCurrent
;
228 (nsIConsoleMessage
**)nsMemory::Alloc((sizeof (nsIConsoleMessage
*))
231 if (messageArray
== nsnull
) {
234 return NS_ERROR_FAILURE
;
239 for (i
= 0; i
< mBufferSize
; i
++) {
240 // if full, fill the buffer starting from mCurrent (which'll be
241 // oldest) wrapping around the buffer to the most recent.
242 messageArray
[i
] = mMessages
[(mCurrent
+ i
) % mBufferSize
];
243 NS_ADDREF(messageArray
[i
]);
246 for (i
= 0; i
< mCurrent
; i
++) {
247 messageArray
[i
] = mMessages
[i
];
248 NS_ADDREF(messageArray
[i
]);
252 *messages
= messageArray
;
258 nsConsoleService::RegisterListener(nsIConsoleListener
*listener
) {
262 * Store a threadsafe proxy to the listener rather than the
263 * listener itself; we want the console service to be callable
264 * from any thread, but listeners can be implemented in
265 * thread-specific ways, and we always want to call them on their
266 * originating thread. JavaScript is the motivating example.
268 nsCOMPtr
<nsIConsoleListener
> proxiedListener
;
270 rv
= GetProxyForListener(listener
, getter_AddRefs(proxiedListener
));
275 nsAutoLock
lock(mLock
);
276 nsISupportsKey
key(listener
);
279 * Put the proxy event listener into a hashtable using the *real*
280 * listener as the key.
282 * This is necessary because proxy objects do *not* maintain
283 * nsISupports identity. Therefore, since GetProxyForListener(...)
284 * can return different proxies for the same object (see bug #85831)
285 * we need to use the real object as the unique key...
287 mListeners
.Put(&key
, proxiedListener
);
293 nsConsoleService::UnregisterListener(nsIConsoleListener
*listener
) {
294 nsAutoLock
lock(mLock
);
296 nsISupportsKey
key(listener
);
297 mListeners
.Remove(&key
);
302 nsConsoleService::GetProxyForListener(nsIConsoleListener
* aListener
,
303 nsIConsoleListener
** aProxy
)
306 * Would it be better to catch that case and leave the listener unproxied?
308 return NS_GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD
,
309 NS_GET_IID(nsIConsoleListener
),
311 NS_PROXY_ASYNC
| NS_PROXY_ALWAYS
,
316 nsConsoleService::Reset()
319 * Make sure nobody trips into the buffer while it's being reset
321 nsAutoLock
lock(mLock
);
327 * Free all messages stored so far (cf. destructor)
329 for (PRUint32 i
= 0; i
< mBufferSize
&& mMessages
[i
] != nsnull
; i
++)
330 NS_RELEASE(mMessages
[i
]);