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
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.
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 ***** */
41 #include "nsIScriptContext.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
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() {
72 virtual void GetLocation(const char **aFileName
, PRUint32
*aLineNo
) {
73 *aFileName
= mFileName
.get();
77 virtual PRUint32
GetScriptTypeID() {
78 return nsIProgrammingLanguage::JAVASCRIPT
;
80 virtual PRUint32
GetScriptVersion() {
84 virtual nsIArray
*GetArgv() {
87 // Called by the timeout mechanism so the secret 'lateness' arg can be
89 virtual void SetLateness(PRIntervalTime aHowLate
);
91 nsresult
Init(nsGlobalWindow
*aWindow
, PRBool
*aIsInterval
,
94 void ReleaseJSObjects();
98 nsCOMPtr
<nsIScriptContext
> mContext
;
100 // filename, line number and JS language version string of the
101 // caller of setTimeout()
105 nsCOMPtr
<nsIArray
> mArgv
;
107 // The JS expression to evaluate or function to call, if !mExpr
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
)
136 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler
)
137 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler
)
139 nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler() :
147 nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler()
153 nsJSScriptTimeoutHandler::ReleaseJSObjects()
155 if (mExpr
|| mFunObj
) {
157 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
159 } else if (mFunObj
) {
160 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
163 NS_WARNING("No func and no expr - roots may not have been removed");
169 nsJSScriptTimeoutHandler::Init(nsGlobalWindow
*aWindow
, PRBool
*aIsInterval
,
172 mContext
= aWindow
->GetContextInternal();
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
);
186 return NS_ERROR_NOT_AVAILABLE
;
188 JSContext
*cx
= nsnull
;
190 rv
= ncc
->GetJSContext(&cx
);
191 NS_ENSURE_SUCCESS(rv
, rv
);
194 jsval
*argv
= nsnull
;
197 ncc
->GetArgvPtr(&argv
);
199 JSString
*expr
= nsnull
;
200 JSObject
*funobj
= nsnull
;
203 JSAutoRequest
ar(cx
);
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
)) {
213 "Second argument to %s must be a millisecond interval",
214 aIsInterval
? kSetIntervalStr
: kSetTimeoutStr
);
215 return NS_ERROR_DOM_TYPE_ERR
;
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]);
231 expr
= ::JS_ValueToString(cx
, argv
[0]);
233 return NS_ERROR_OUT_OF_MEMORY
;
234 argv
[0] = STRING_TO_JSVAL(expr
);
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
;
246 rv
= NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
247 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
259 rv
= NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
260 NS_ENSURE_SUCCESS(rv
, rv
);
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
));
271 return NS_ERROR_OUT_OF_MEMORY
;
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.
287 NS_WARNING("No func and no expr - why are we here?");
289 *aInterval
= interval
;
293 void nsJSScriptTimeoutHandler::SetLateness(PRIntervalTime aHowLate
)
295 nsCOMPtr
<nsIJSArgArray
> jsarray(do_QueryInterface(mArgv
));
299 jsarray
->GetArgs(&argc
, reinterpret_cast<void **>(&jsargv
));
301 jsargv
[argc
-1] = INT_TO_JSVAL((jsint
) aHowLate
);
303 NS_ERROR("How can our argv not handle this?");
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
,
318 nsIScriptTimeoutHandler
**aRet
)
321 nsJSScriptTimeoutHandler
*handler
= new nsJSScriptTimeoutHandler();
323 return NS_ERROR_OUT_OF_MEMORY
;
325 nsresult rv
= handler
->Init(aWindow
, aIsInterval
, aInterval
);
331 NS_ADDREF(*aRet
= handler
);