Bug 572417 - Release mouse capture in flash subclass after mouse events get delivered...
[mozilla-central.git] / xpcom / base / nsExceptionService.cpp
blob342239916e515e16cb031db145ab092e31bea5e4
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 * ActiveState Tool Corp..
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Mark Hammond <MarkH@ActiveState.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 ***** */
39 #include "nsISupports.h"
40 #include "nsExceptionService.h"
41 #include "nsIServiceManager.h"
42 #include "nsCOMPtr.h"
43 #include "prthread.h"
44 #include "prlock.h"
45 #include "mozilla/Services.h"
47 static const PRUintn BAD_TLS_INDEX = (PRUintn) -1;
49 #define CHECK_SERVICE_USE_OK() if (!lock) return NS_ERROR_NOT_INITIALIZED
50 #define CHECK_MANAGER_USE_OK() if (!mService || !nsExceptionService::lock) return NS_ERROR_NOT_INITIALIZED
52 // A key for our registered module providers hashtable
53 class nsProviderKey : public nsHashKey {
54 protected:
55 PRUint32 mKey;
56 public:
57 nsProviderKey(PRUint32 key) : mKey(key) {}
58 PRUint32 HashCode(void) const {
59 return mKey;
61 PRBool Equals(const nsHashKey *aKey) const {
62 return mKey == ((const nsProviderKey *) aKey)->mKey;
64 nsHashKey *Clone() const {
65 return new nsProviderKey(mKey);
67 PRUint32 GetValue() { return mKey; }
70 /** Exception Manager definition **/
71 class nsExceptionManager : public nsIExceptionManager
73 public:
74 NS_DECL_ISUPPORTS
75 NS_DECL_NSIEXCEPTIONMANAGER
77 nsExceptionManager(nsExceptionService *svc);
78 /* additional members */
79 nsCOMPtr<nsIException> mCurrentException;
80 nsExceptionManager *mNextThread; // not ref-counted.
81 nsExceptionService *mService; // not ref-counted
82 #ifdef NS_DEBUG
83 static PRInt32 totalInstances;
84 #endif
86 private:
87 ~nsExceptionManager();
91 #ifdef NS_DEBUG
92 PRInt32 nsExceptionManager::totalInstances = 0;
93 #endif
95 // Note this object is single threaded - the service itself ensures
96 // one per thread.
97 // An exception if the destructor, which may be called on
98 // the thread shutting down xpcom
99 NS_IMPL_ISUPPORTS1(nsExceptionManager, nsIExceptionManager)
101 nsExceptionManager::nsExceptionManager(nsExceptionService *svc) :
102 mNextThread(nsnull),
103 mService(svc)
105 /* member initializers and constructor code */
106 #ifdef NS_DEBUG
107 PR_AtomicIncrement(&totalInstances);
108 #endif
111 nsExceptionManager::~nsExceptionManager()
113 /* destructor code */
114 #ifdef NS_DEBUG
115 PR_AtomicDecrement(&totalInstances);
116 #endif // NS_DEBUG
119 /* void setCurrentException (in nsIException error); */
120 NS_IMETHODIMP nsExceptionManager::SetCurrentException(nsIException *error)
122 CHECK_MANAGER_USE_OK();
123 mCurrentException = error;
124 return NS_OK;
127 /* nsIException getCurrentException (); */
128 NS_IMETHODIMP nsExceptionManager::GetCurrentException(nsIException **_retval)
130 CHECK_MANAGER_USE_OK();
131 *_retval = mCurrentException;
132 NS_IF_ADDREF(*_retval);
133 return NS_OK;
136 /* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
137 NS_IMETHODIMP nsExceptionManager::GetExceptionFromProvider(nsresult rc, nsIException * defaultException, nsIException **_retval)
139 CHECK_MANAGER_USE_OK();
140 // Just delegate back to the service with the provider map.
141 return mService->GetExceptionFromProvider(rc, defaultException, _retval);
144 /* The Exception Service */
146 PRUintn nsExceptionService::tlsIndex = BAD_TLS_INDEX;
147 PRLock *nsExceptionService::lock = nsnull;
148 nsExceptionManager *nsExceptionService::firstThread = nsnull;
150 #ifdef NS_DEBUG
151 PRInt32 nsExceptionService::totalInstances = 0;
152 #endif
154 NS_IMPL_THREADSAFE_ISUPPORTS3(nsExceptionService,
155 nsIExceptionService,
156 nsIExceptionManager,
157 nsIObserver)
159 nsExceptionService::nsExceptionService()
160 : mProviders(4, PR_TRUE) /* small, thread-safe hashtable */
162 #ifdef NS_DEBUG
163 if (PR_AtomicIncrement(&totalInstances)!=1) {
164 NS_ERROR("The nsExceptionService is a singleton!");
166 #endif
167 /* member initializers and constructor code */
168 if (tlsIndex == BAD_TLS_INDEX) {
169 PRStatus status;
170 status = PR_NewThreadPrivateIndex( &tlsIndex, ThreadDestruct );
171 NS_ASSERTION(status==0, "ScriptErrorService could not allocate TLS storage.");
173 lock = PR_NewLock();
174 NS_ASSERTION(lock, "Error allocating ExceptionService lock");
176 // observe XPCOM shutdown.
177 nsCOMPtr<nsIObserverService> observerService =
178 mozilla::services::GetObserverService();
179 NS_ASSERTION(observerService, "Could not get observer service!");
180 if (observerService)
181 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
184 nsExceptionService::~nsExceptionService()
186 Shutdown();
187 /* destructor code */
188 #ifdef NS_DEBUG
189 PR_AtomicDecrement(&totalInstances);
190 #endif
193 /*static*/
194 void nsExceptionService::ThreadDestruct( void *data )
196 if (!lock) {
197 NS_WARNING("nsExceptionService ignoring thread destruction after shutdown");
198 return;
200 DropThread( (nsExceptionManager *)data );
204 void nsExceptionService::Shutdown()
206 mProviders.Reset();
207 if (lock) {
208 DropAllThreads();
209 PR_DestroyLock(lock);
210 lock = nsnull;
212 PR_SetThreadPrivate(tlsIndex, nsnull);
215 /* void setCurrentException (in nsIException error); */
216 NS_IMETHODIMP nsExceptionService::SetCurrentException(nsIException *error)
218 CHECK_SERVICE_USE_OK();
219 nsCOMPtr<nsIExceptionManager> sm;
220 nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
221 if (NS_FAILED(nr))
222 return nr;
223 return sm->SetCurrentException(error);
226 /* nsIException getCurrentException (); */
227 NS_IMETHODIMP nsExceptionService::GetCurrentException(nsIException **_retval)
229 CHECK_SERVICE_USE_OK();
230 nsCOMPtr<nsIExceptionManager> sm;
231 nsresult nr = GetCurrentExceptionManager(getter_AddRefs(sm));
232 if (NS_FAILED(nr))
233 return nr;
234 return sm->GetCurrentException(_retval);
237 /* nsIException getExceptionFromProvider( in nsresult rc, in nsIException defaultException); */
238 NS_IMETHODIMP nsExceptionService::GetExceptionFromProvider(nsresult rc,
239 nsIException * defaultException, nsIException **_retval)
241 CHECK_SERVICE_USE_OK();
242 return DoGetExceptionFromProvider(rc, defaultException, _retval);
245 /* readonly attribute nsIExceptionManager currentExceptionManager; */
246 NS_IMETHODIMP nsExceptionService::GetCurrentExceptionManager(nsIExceptionManager * *aCurrentScriptManager)
248 CHECK_SERVICE_USE_OK();
249 nsExceptionManager *mgr = (nsExceptionManager *)PR_GetThreadPrivate(tlsIndex);
250 if (mgr == nsnull) {
251 // Stick the new exception object in with no reference count.
252 mgr = new nsExceptionManager(this);
253 if (mgr == nsnull)
254 return NS_ERROR_OUT_OF_MEMORY;
255 PR_SetThreadPrivate(tlsIndex, mgr);
256 // The reference count is held in the thread-list
257 AddThread(mgr);
259 *aCurrentScriptManager = mgr;
260 NS_ADDREF(*aCurrentScriptManager);
261 return NS_OK;
264 /* void registerErrorProvider (in nsIExceptionProvider provider, in PRUint32 moduleCode); */
265 NS_IMETHODIMP nsExceptionService::RegisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
267 CHECK_SERVICE_USE_OK();
269 nsProviderKey key(errorModule);
270 if (mProviders.Put(&key, provider)) {
271 NS_WARNING("Registration of exception provider overwrote another provider with the same module code!");
273 return NS_OK;
276 /* void unregisterErrorProvider (in nsIExceptionProvider provider, in PRUint32 errorModule); */
277 NS_IMETHODIMP nsExceptionService::UnregisterExceptionProvider(nsIExceptionProvider *provider, PRUint32 errorModule)
279 CHECK_SERVICE_USE_OK();
280 nsProviderKey key(errorModule);
281 if (!mProviders.Remove(&key)) {
282 NS_WARNING("Attempt to unregister an unregistered exception provider!");
283 return NS_ERROR_UNEXPECTED;
285 return NS_OK;
288 // nsIObserver
289 NS_IMETHODIMP nsExceptionService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
291 Shutdown();
292 return NS_OK;
295 nsresult
296 nsExceptionService::DoGetExceptionFromProvider(nsresult errCode,
297 nsIException * defaultException,
298 nsIException **_exc)
300 // Check for an existing exception
301 nsresult nr = GetCurrentException(_exc);
302 if (NS_SUCCEEDED(nr) && *_exc) {
303 (*_exc)->GetResult(&nr);
304 // If it matches our result then use it
305 if (nr == errCode)
306 return NS_OK;
307 NS_RELEASE(*_exc);
309 nsProviderKey key(NS_ERROR_GET_MODULE(errCode));
310 nsCOMPtr<nsIExceptionProvider> provider =
311 dont_AddRef((nsIExceptionProvider *)mProviders.Get(&key));
313 // No provider so we'll return the default exception
314 if (!provider) {
315 *_exc = defaultException;
316 NS_IF_ADDREF(*_exc);
317 return NS_OK;
320 return provider->GetException(errCode, defaultException, _exc);
323 // thread management
324 /*static*/ void nsExceptionService::AddThread(nsExceptionManager *thread)
326 PR_Lock(lock);
327 thread->mNextThread = firstThread;
328 firstThread = thread;
329 NS_ADDREF(thread);
330 PR_Unlock(lock);
333 /*static*/ void nsExceptionService::DoDropThread(nsExceptionManager *thread)
335 nsExceptionManager **emp = &firstThread;
336 while (*emp != thread) {
337 NS_ABORT_IF_FALSE(*emp, "Could not find the thread to drop!");
338 emp = &(*emp)->mNextThread;
340 *emp = thread->mNextThread;
341 NS_RELEASE(thread);
344 /*static*/ void nsExceptionService::DropThread(nsExceptionManager *thread)
346 PR_Lock(lock);
347 DoDropThread(thread);
348 PR_Unlock(lock);
351 /*static*/ void nsExceptionService::DropAllThreads()
353 PR_Lock(lock);
354 while (firstThread)
355 DoDropThread(firstThread);
356 PR_Unlock(lock);