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 "nsSupportsArray.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_QUERY_INTERFACE1_CI(nsConsoleService
, nsIConsoleService
)
59 NS_IMPL_CI_INTERFACE_GETTER1(nsConsoleService
, nsIConsoleService
)
61 nsConsoleService::nsConsoleService()
62 : mMessages(nsnull
), mCurrent(0), mFull(PR_FALSE
), mListening(PR_FALSE
), mLock(nsnull
)
64 // XXX grab this from a pref!
65 // hm, but worry about circularity, bc we want to be able to report
70 nsConsoleService::~nsConsoleService()
73 while (i
< mBufferSize
&& mMessages
[i
] != nsnull
) {
74 NS_RELEASE(mMessages
[i
]);
79 if (mListeners
.Count() != 0) {
81 "WARNING - %d console error listeners still registered!\n"
82 "More calls to nsIConsoleService::UnregisterListener needed.\n",
89 nsMemory::Free(mMessages
);
91 PR_DestroyLock(mLock
);
95 nsConsoleService::Init()
97 mMessages
= (nsIConsoleMessage
**)
98 nsMemory::Alloc(mBufferSize
* sizeof(nsIConsoleMessage
*));
100 return NS_ERROR_OUT_OF_MEMORY
;
102 // Array elements should be 0 initially for circular buffer algorithm.
103 memset(mMessages
, 0, mBufferSize
* sizeof(nsIConsoleMessage
*));
105 mLock
= PR_NewLock();
107 return NS_ERROR_OUT_OF_MEMORY
;
112 static PRBool PR_CALLBACK
snapshot_enum_func(nsHashKey
*key
, void *data
, void* closure
)
114 nsISupportsArray
*array
= (nsISupportsArray
*)closure
;
116 // Copy each element into the temporary nsSupportsArray...
117 array
->AppendElement((nsISupports
*)data
);
121 // nsIConsoleService methods
123 nsConsoleService::LogMessage(nsIConsoleMessage
*message
)
125 if (message
== nsnull
)
126 return NS_ERROR_INVALID_ARG
;
128 nsSupportsArray listenersSnapshot
;
129 nsIConsoleMessage
*retiredMessage
;
131 NS_ADDREF(message
); // early, in case it's same as replaced below.
134 * Lock while updating buffer, and while taking snapshot of
138 nsAutoLock
lock(mLock
);
141 * If there's already a message in the slot we're about to replace,
142 * we've wrapped around, and we need to release the old message. We
143 * save a pointer to it, so we can release below outside the lock.
145 retiredMessage
= mMessages
[mCurrent
];
147 mMessages
[mCurrent
++] = message
;
148 if (mCurrent
== mBufferSize
) {
149 mCurrent
= 0; // wrap around.
154 * Copy the listeners into the snapshot array - in case a listener
155 * is removed during an Observe(...) notification...
157 mListeners
.Enumerate(snapshot_enum_func
, &listenersSnapshot
);
159 if (retiredMessage
!= nsnull
)
160 NS_RELEASE(retiredMessage
);
163 * Iterate through any registered listeners and tell them about
164 * the message. We use the mListening flag to guard against
165 * recursive message logs. This could sometimes result in
166 * listeners being skipped because of activity on other threads,
167 * when we only care about the recursive case.
169 nsCOMPtr
<nsIConsoleListener
> listener
;
171 nsresult returned_rv
;
172 PRUint32 snapshotCount
;
173 rv
= listenersSnapshot
.Count(&snapshotCount
);
178 nsAutoLock
lock(mLock
);
181 mListening
= PR_TRUE
;
185 for (PRUint32 i
= 0; i
< snapshotCount
; i
++) {
186 rv
= listenersSnapshot
.GetElementAt(i
, getter_AddRefs(listener
));
189 break; // fall thru to mListening restore code below.
191 listener
->Observe(message
);
195 nsAutoLock
lock(mLock
);
196 mListening
= PR_FALSE
;
203 nsConsoleService::LogStringMessage(const PRUnichar
*message
)
205 nsConsoleMessage
*msg
= new nsConsoleMessage(message
);
206 return this->LogMessage(msg
);
210 nsConsoleService::GetMessageArray(nsIConsoleMessage
***messages
, PRUint32
*count
)
212 nsIConsoleMessage
**messageArray
;
215 * Lock the whole method, as we don't want anyone mucking with mCurrent or
216 * mFull while we're copying out the buffer.
218 nsAutoLock
lock(mLock
);
220 if (mCurrent
== 0 && !mFull
) {
222 * Make a 1-length output array so that nobody gets confused,
223 * and return a count of 0. This should result in a 0-length
224 * array object when called from script.
226 messageArray
= (nsIConsoleMessage
**)
227 nsMemory::Alloc(sizeof (nsIConsoleMessage
*));
228 *messageArray
= nsnull
;
229 *messages
= messageArray
;
235 PRUint32 resultSize
= mFull
? mBufferSize
: mCurrent
;
237 (nsIConsoleMessage
**)nsMemory::Alloc((sizeof (nsIConsoleMessage
*))
240 if (messageArray
== nsnull
) {
243 return NS_ERROR_FAILURE
;
248 for (i
= 0; i
< mBufferSize
; i
++) {
249 // if full, fill the buffer starting from mCurrent (which'll be
250 // oldest) wrapping around the buffer to the most recent.
251 messageArray
[i
] = mMessages
[(mCurrent
+ i
) % mBufferSize
];
252 NS_ADDREF(messageArray
[i
]);
255 for (i
= 0; i
< mCurrent
; i
++) {
256 messageArray
[i
] = mMessages
[i
];
257 NS_ADDREF(messageArray
[i
]);
261 *messages
= messageArray
;
267 nsConsoleService::RegisterListener(nsIConsoleListener
*listener
) {
271 * Store a threadsafe proxy to the listener rather than the
272 * listener itself; we want the console service to be callable
273 * from any thread, but listeners can be implemented in
274 * thread-specific ways, and we always want to call them on their
275 * originating thread. JavaScript is the motivating example.
277 nsCOMPtr
<nsIConsoleListener
> proxiedListener
;
279 rv
= GetProxyForListener(listener
, getter_AddRefs(proxiedListener
));
284 nsAutoLock
lock(mLock
);
285 nsISupportsKey
key(listener
);
288 * Put the proxy event listener into a hashtable using the *real*
289 * listener as the key.
291 * This is necessary because proxy objects do *not* maintain
292 * nsISupports identity. Therefore, since GetProxyForListener(...)
293 * can return different proxies for the same object (see bug #85831)
294 * we need to use the real object as the unique key...
296 mListeners
.Put(&key
, proxiedListener
);
302 nsConsoleService::UnregisterListener(nsIConsoleListener
*listener
) {
303 nsAutoLock
lock(mLock
);
305 nsISupportsKey
key(listener
);
306 mListeners
.Remove(&key
);
311 nsConsoleService::GetProxyForListener(nsIConsoleListener
* aListener
,
312 nsIConsoleListener
** aProxy
)
315 * Would it be better to catch that case and leave the listener unproxied?
317 return NS_GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD
,
318 NS_GET_IID(nsIConsoleListener
),
320 NS_PROXY_ASYNC
| NS_PROXY_ALWAYS
,
325 nsConsoleService::Reset()
328 * Make sure nobody trips into the buffer while it's being reset
330 nsAutoLock
lock(mLock
);
336 * Free all messages stored so far (cf. destructor)
338 for (PRUint32 i
= 0; i
< mBufferSize
&& mMessages
[i
] != nsnull
; i
++)
339 NS_RELEASE(mMessages
[i
]);