Bug 464858 - intermittent / recurring fail of test_db_update_v1.js. bustagefix.
[wine-gecko.git] / dom / src / base / nsJSTimeoutHandler.cpp
blob4d66818c76f31cb4a699877e02b562827efa08ff
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Mark Hammond <mhammond@skippinet.com.au>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsCOMPtr.h"
41 #include "nsIScriptContext.h"
42 #include "nsIArray.h"
43 #include "nsIScriptTimeoutHandler.h"
44 #include "nsIXPConnect.h"
45 #include "nsIJSRuntimeService.h"
46 #include "nsJSUtils.h"
47 #include "nsDOMJSUtils.h"
48 #include "nsContentUtils.h"
49 #include "nsJSEnvironment.h"
50 #include "nsServiceManagerUtils.h"
51 #include "nsDOMError.h"
52 #include "nsGlobalWindow.h"
54 static const char kSetIntervalStr[] = "setInterval";
55 static const char kSetTimeoutStr[] = "setTimeout";
57 // Our JS nsIScriptTimeoutHandler implementation.
58 class nsJSScriptTimeoutHandler: public nsIScriptTimeoutHandler
60 public:
61 // nsISupports
62 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
63 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsJSScriptTimeoutHandler)
65 nsJSScriptTimeoutHandler();
66 ~nsJSScriptTimeoutHandler();
68 virtual const PRUnichar *GetHandlerText();
69 virtual void *GetScriptObject() {
70 return mFunObj;
72 virtual void GetLocation(const char **aFileName, PRUint32 *aLineNo) {
73 *aFileName = mFileName.get();
74 *aLineNo = mLineNo;
77 virtual PRUint32 GetScriptTypeID() {
78 return nsIProgrammingLanguage::JAVASCRIPT;
80 virtual PRUint32 GetScriptVersion() {
81 return mVersion;
84 virtual nsIArray *GetArgv() {
85 return mArgv;
87 // Called by the timeout mechanism so the secret 'lateness' arg can be
88 // added.
89 virtual void SetLateness(PRIntervalTime aHowLate);
91 nsresult Init(nsGlobalWindow *aWindow, PRBool *aIsInterval,
92 PRInt32 *aInterval);
94 void ReleaseJSObjects();
96 private:
98 nsCOMPtr<nsIScriptContext> mContext;
100 // filename, line number and JS language version string of the
101 // caller of setTimeout()
102 nsCString mFileName;
103 PRUint32 mLineNo;
104 PRUint32 mVersion;
105 nsCOMPtr<nsIArray> mArgv;
107 // The JS expression to evaluate or function to call, if !mExpr
108 JSString *mExpr;
109 JSObject *mFunObj;
113 // nsJSScriptTimeoutHandler
114 // QueryInterface implementation for nsJSScriptTimeoutHandler
115 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
116 NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSScriptTimeoutHandler)
117 tmp->ReleaseJSObjects();
118 NS_IMPL_CYCLE_COLLECTION_ROOT_END
119 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsJSScriptTimeoutHandler)
120 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSScriptTimeoutHandler)
121 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
122 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgv)
123 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
124 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
126 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
127 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mExpr)
128 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFunObj)
129 NS_IMPL_CYCLE_COLLECTION_TRACE_END
131 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler)
132 NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler)
133 NS_INTERFACE_MAP_ENTRY(nsISupports)
134 NS_INTERFACE_MAP_END
136 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler)
137 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler)
139 nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler() :
140 mLineNo(0),
141 mVersion(nsnull),
142 mExpr(nsnull),
143 mFunObj(nsnull)
147 nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler()
149 ReleaseJSObjects();
152 void
153 nsJSScriptTimeoutHandler::ReleaseJSObjects()
155 if (mExpr || mFunObj) {
156 if (mExpr) {
157 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
158 mExpr = nsnull;
159 } else if (mFunObj) {
160 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
161 mFunObj = nsnull;
162 } else {
163 NS_WARNING("No func and no expr - roots may not have been removed");
168 nsresult
169 nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, PRBool *aIsInterval,
170 PRInt32 *aInterval)
172 mContext = aWindow->GetContextInternal();
173 if (!mContext) {
174 // This window was already closed, or never properly initialized,
175 // don't let a timer be scheduled on such a window.
177 return NS_ERROR_NOT_INITIALIZED;
180 nsAXPCNativeCallContext *ncc = nsnull;
181 nsresult rv = nsContentUtils::XPConnect()->
182 GetCurrentNativeCallContext(&ncc);
183 NS_ENSURE_SUCCESS(rv, rv);
185 if (!ncc)
186 return NS_ERROR_NOT_AVAILABLE;
188 JSContext *cx = nsnull;
190 rv = ncc->GetJSContext(&cx);
191 NS_ENSURE_SUCCESS(rv, rv);
193 PRUint32 argc;
194 jsval *argv = nsnull;
196 ncc->GetArgc(&argc);
197 ncc->GetArgvPtr(&argv);
199 JSString *expr = nsnull;
200 JSObject *funobj = nsnull;
201 int32 interval = 0;
203 JSAutoRequest ar(cx);
205 if (argc < 1) {
206 ::JS_ReportError(cx, "Function %s requires at least 1 parameter",
207 *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
208 return NS_ERROR_DOM_TYPE_ERR;
211 if (argc > 1 && !::JS_ValueToECMAInt32(cx, argv[1], &interval)) {
212 ::JS_ReportError(cx,
213 "Second argument to %s must be a millisecond interval",
214 aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
215 return NS_ERROR_DOM_TYPE_ERR;
218 if (argc == 1) {
219 // If no interval was specified, treat this like a timeout, to avoid
220 // setting an interval of 0 milliseconds.
221 *aIsInterval = PR_FALSE;
224 switch (::JS_TypeOfValue(cx, argv[0])) {
225 case JSTYPE_FUNCTION:
226 funobj = JSVAL_TO_OBJECT(argv[0]);
227 break;
229 case JSTYPE_STRING:
230 case JSTYPE_OBJECT:
231 expr = ::JS_ValueToString(cx, argv[0]);
232 if (!expr)
233 return NS_ERROR_OUT_OF_MEMORY;
234 argv[0] = STRING_TO_JSVAL(expr);
235 break;
237 default:
238 ::JS_ReportError(cx, "useless %s call (missing quotes around argument?)",
239 *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
241 // Return an error that nsGlobalWindow can recognize and turn into NS_OK.
242 return NS_ERROR_DOM_TYPE_ERR;
245 if (expr) {
246 rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
247 NS_ENSURE_SUCCESS(rv, rv);
249 mExpr = expr;
251 nsIPrincipal *prin = aWindow->GetPrincipal();
253 // Get the calling location.
254 const char *filename;
255 if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo, prin)) {
256 mFileName.Assign(filename);
258 } else if (funobj) {
259 rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
260 NS_ENSURE_SUCCESS(rv, rv);
262 mFunObj = funobj;
264 // Create our arg array - leave an extra slot for a secret final argument
265 // that indicates to the called function how "late" the timeout is. We
266 // will fill that in when SetLateness is called.
267 nsCOMPtr<nsIArray> array;
268 rv = NS_CreateJSArgv(cx, (argc > 1) ? argc - 1 : argc, nsnull,
269 getter_AddRefs(array));
270 if (NS_FAILED(rv)) {
271 return NS_ERROR_OUT_OF_MEMORY;
274 PRUint32 dummy;
275 jsval *jsargv = nsnull;
276 nsCOMPtr<nsIJSArgArray> jsarray(do_QueryInterface(array));
277 jsarray->GetArgs(&dummy, reinterpret_cast<void **>(&jsargv));
279 // must have worked - we own the impl! :)
280 NS_ASSERTION(jsargv, "No argv!");
281 for (PRInt32 i = 2; (PRUint32)i < argc; ++i) {
282 jsargv[i - 2] = argv[i];
284 // final arg slot remains null, array has rooted vals.
285 mArgv = array;
286 } else {
287 NS_WARNING("No func and no expr - why are we here?");
289 *aInterval = interval;
290 return NS_OK;
293 void nsJSScriptTimeoutHandler::SetLateness(PRIntervalTime aHowLate)
295 nsCOMPtr<nsIJSArgArray> jsarray(do_QueryInterface(mArgv));
296 if (jsarray) {
297 PRUint32 argc;
298 jsval *jsargv;
299 jsarray->GetArgs(&argc, reinterpret_cast<void **>(&jsargv));
300 if (jsargv && argc)
301 jsargv[argc-1] = INT_TO_JSVAL((jsint) aHowLate);
302 } else {
303 NS_ERROR("How can our argv not handle this?");
307 const PRUnichar *
308 nsJSScriptTimeoutHandler::GetHandlerText()
310 NS_ASSERTION(mExpr, "No expression, so no handler text!");
311 return reinterpret_cast<const PRUnichar *>
312 (::JS_GetStringChars(mExpr));
315 nsresult NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
316 PRBool *aIsInterval,
317 PRInt32 *aInterval,
318 nsIScriptTimeoutHandler **aRet)
320 *aRet = nsnull;
321 nsJSScriptTimeoutHandler *handler = new nsJSScriptTimeoutHandler();
322 if (!handler)
323 return NS_ERROR_OUT_OF_MEMORY;
325 nsresult rv = handler->Init(aWindow, aIsInterval, aInterval);
326 if (NS_FAILED(rv)) {
327 delete handler;
328 return rv;
331 NS_ADDREF(*aRet = handler);
333 return NS_OK;