Bug 572417 - Release mouse capture in flash subclass after mouse events get delivered...
[mozilla-central.git] / xpcom / base / nsConsoleService.cpp
blob5e6ce351572ad301aaab6119d33b727c114f4f9a
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
13 * License.
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.
22 * Contributor(s):
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.
44 /* Threadsafe. */
46 #include "nsMemory.h"
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
67 // prefs errs...
68 mBufferSize = 250;
71 nsConsoleService::~nsConsoleService()
73 PRUint32 i = 0;
74 while (i < mBufferSize && mMessages[i] != nsnull) {
75 NS_RELEASE(mMessages[i]);
76 i++;
79 #ifdef DEBUG_mccabe
80 if (mListeners.Count() != 0) {
81 fprintf(stderr,
82 "WARNING - %d console error listeners still registered!\n"
83 "More calls to nsIConsoleService::UnregisterListener needed.\n",
84 mListeners.Count());
87 #endif
89 if (mMessages)
90 nsMemory::Free(mMessages);
91 if (mLock)
92 PR_DestroyLock(mLock);
95 nsresult
96 nsConsoleService::Init()
98 mMessages = (nsIConsoleMessage **)
99 nsMemory::Alloc(mBufferSize * sizeof(nsIConsoleMessage *));
100 if (!mMessages)
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();
107 if (!mLock)
108 return NS_ERROR_OUT_OF_MEMORY;
110 return NS_OK;
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);
120 return PR_TRUE;
123 // nsIConsoleService methods
124 NS_IMETHODIMP
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
137 * listeners array.
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.
152 mFull = PR_TRUE;
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);
176 if (mListening)
177 return NS_OK;
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;
190 return NS_OK;
193 NS_IMETHODIMP
194 nsConsoleService::LogStringMessage(const PRUnichar *message)
196 nsConsoleMessage *msg = new nsConsoleMessage(message);
197 return this->LogMessage(msg);
200 NS_IMETHODIMP
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;
221 *count = 0;
223 return NS_OK;
226 PRUint32 resultSize = mFull ? mBufferSize : mCurrent;
227 messageArray =
228 (nsIConsoleMessage **)nsMemory::Alloc((sizeof (nsIConsoleMessage *))
229 * resultSize);
231 if (messageArray == nsnull) {
232 *messages = nsnull;
233 *count = 0;
234 return NS_ERROR_FAILURE;
237 PRUint32 i;
238 if (mFull) {
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]);
245 } else {
246 for (i = 0; i < mCurrent; i++) {
247 messageArray[i] = mMessages[i];
248 NS_ADDREF(messageArray[i]);
251 *count = resultSize;
252 *messages = messageArray;
254 return NS_OK;
257 NS_IMETHODIMP
258 nsConsoleService::RegisterListener(nsIConsoleListener *listener) {
259 nsresult rv;
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));
271 if (NS_FAILED(rv))
272 return rv;
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);
289 return NS_OK;
292 NS_IMETHODIMP
293 nsConsoleService::UnregisterListener(nsIConsoleListener *listener) {
294 nsAutoLock lock(mLock);
296 nsISupportsKey key(listener);
297 mListeners.Remove(&key);
298 return NS_OK;
301 nsresult
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),
310 aListener,
311 NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
312 (void**) aProxy);
315 NS_IMETHODIMP
316 nsConsoleService::Reset()
319 * Make sure nobody trips into the buffer while it's being reset
321 nsAutoLock lock(mLock);
323 mCurrent = 0;
324 mFull = PR_FALSE;
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]);
332 return NS_OK;