Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / dom / base / nsJSTimeoutHandler.cpp
blobe11f7217a58ced1239b2f95de5ead97da5e2b7d4
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"
53 #include "jsobj.h"
54 #include "jsatom.h"
55 #include "jsfun.h"
56 #include "nsIContentSecurityPolicy.h"
58 static const char kSetIntervalStr[] = "setInterval";
59 static const char kSetTimeoutStr[] = "setTimeout";
61 // Our JS nsIScriptTimeoutHandler implementation.
62 class nsJSScriptTimeoutHandler: public nsIScriptTimeoutHandler
64 public:
65 // nsISupports
66 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
67 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsJSScriptTimeoutHandler)
69 nsJSScriptTimeoutHandler();
70 ~nsJSScriptTimeoutHandler();
72 virtual const PRUnichar *GetHandlerText();
73 virtual void *GetScriptObject() {
74 return mFunObj;
76 virtual void GetLocation(const char **aFileName, PRUint32 *aLineNo) {
77 *aFileName = mFileName.get();
78 *aLineNo = mLineNo;
81 virtual PRUint32 GetScriptTypeID() {
82 return nsIProgrammingLanguage::JAVASCRIPT;
84 virtual PRUint32 GetScriptVersion() {
85 return mVersion;
88 virtual nsIArray *GetArgv() {
89 return mArgv;
91 // Called by the timeout mechanism so the secret 'lateness' arg can be
92 // added.
93 virtual void SetLateness(PRIntervalTime aHowLate);
95 nsresult Init(nsGlobalWindow *aWindow, PRBool *aIsInterval,
96 PRInt32 *aInterval);
98 void ReleaseJSObjects();
100 private:
102 nsCOMPtr<nsIScriptContext> mContext;
104 // filename, line number and JS language version string of the
105 // caller of setTimeout()
106 nsCString mFileName;
107 PRUint32 mLineNo;
108 PRUint32 mVersion;
109 nsCOMPtr<nsIArray> mArgv;
111 // The JS expression to evaluate or function to call, if !mExpr
112 JSFlatString *mExpr;
113 JSObject *mFunObj;
117 // nsJSScriptTimeoutHandler
118 // QueryInterface implementation for nsJSScriptTimeoutHandler
119 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler)
120 NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSScriptTimeoutHandler)
121 tmp->ReleaseJSObjects();
122 NS_IMPL_CYCLE_COLLECTION_ROOT_END
123 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsJSScriptTimeoutHandler)
124 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSScriptTimeoutHandler)
125 if (NS_UNLIKELY(cb.WantDebugInfo())) {
126 nsCAutoString foo("nsJSScriptTimeoutHandler");
127 if (tmp->mExpr) {
128 foo.AppendLiteral(" [");
129 foo.Append(tmp->mFileName);
130 foo.AppendLiteral(":");
131 foo.AppendInt(tmp->mLineNo);
132 foo.AppendLiteral("]");
134 else if (tmp->mFunObj) {
135 JSFunction* fun = (JSFunction*)tmp->mFunObj->getPrivate();
136 if (fun->atom) {
137 size_t size = 1 + JS_PutEscapedFlatString(NULL, 0, ATOM_TO_STRING(fun->atom), 0);
138 char *name = new char[size];
139 if (name) {
140 JS_PutEscapedFlatString(name, size, ATOM_TO_STRING(fun->atom), 0);
141 foo.AppendLiteral(" [");
142 foo.Append(name);
143 delete[] name;
144 foo.AppendLiteral("]");
148 cb.DescribeNode(RefCounted, tmp->mRefCnt.get(),
149 sizeof(nsJSScriptTimeoutHandler), foo.get());
151 else {
152 cb.DescribeNode(RefCounted, tmp->mRefCnt.get(),
153 sizeof(nsJSScriptTimeoutHandler),
154 "nsJSScriptTimeoutHandler");
157 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgv)
159 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
160 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
162 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
163 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mExpr)
164 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFunObj)
165 NS_IMPL_CYCLE_COLLECTION_TRACE_END
167 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler)
168 NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler)
169 NS_INTERFACE_MAP_ENTRY(nsISupports)
170 NS_INTERFACE_MAP_END
172 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler)
173 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler)
175 nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler() :
176 mLineNo(0),
177 mVersion(nsnull),
178 mExpr(nsnull),
179 mFunObj(nsnull)
183 nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler()
185 ReleaseJSObjects();
188 void
189 nsJSScriptTimeoutHandler::ReleaseJSObjects()
191 if (mExpr || mFunObj) {
192 if (mExpr) {
193 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
194 mExpr = nsnull;
195 } else if (mFunObj) {
196 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
197 mFunObj = nsnull;
198 } else {
199 NS_WARNING("No func and no expr - roots may not have been removed");
204 nsresult
205 nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, PRBool *aIsInterval,
206 PRInt32 *aInterval)
208 mContext = aWindow->GetContextInternal();
209 if (!mContext) {
210 // This window was already closed, or never properly initialized,
211 // don't let a timer be scheduled on such a window.
213 return NS_ERROR_NOT_INITIALIZED;
216 nsAXPCNativeCallContext *ncc = nsnull;
217 nsresult rv = nsContentUtils::XPConnect()->
218 GetCurrentNativeCallContext(&ncc);
219 NS_ENSURE_SUCCESS(rv, rv);
221 if (!ncc)
222 return NS_ERROR_NOT_AVAILABLE;
224 JSContext *cx = nsnull;
226 rv = ncc->GetJSContext(&cx);
227 NS_ENSURE_SUCCESS(rv, rv);
229 PRUint32 argc;
230 jsval *argv = nsnull;
232 ncc->GetArgc(&argc);
233 ncc->GetArgvPtr(&argv);
235 JSFlatString *expr = nsnull;
236 JSObject *funobj = nsnull;
237 int32 interval = 0;
239 JSAutoRequest ar(cx);
241 if (argc < 1) {
242 ::JS_ReportError(cx, "Function %s requires at least 2 parameter",
243 *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
244 return NS_ERROR_DOM_TYPE_ERR;
247 if (argc > 1 && !::JS_ValueToECMAInt32(cx, argv[1], &interval)) {
248 ::JS_ReportError(cx,
249 "Second argument to %s must be a millisecond interval",
250 aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
251 return NS_ERROR_DOM_TYPE_ERR;
254 if (argc == 1) {
255 // If no interval was specified, treat this like a timeout, to avoid
256 // setting an interval of 0 milliseconds.
257 *aIsInterval = PR_FALSE;
260 switch (::JS_TypeOfValue(cx, argv[0])) {
261 case JSTYPE_FUNCTION:
262 funobj = JSVAL_TO_OBJECT(argv[0]);
263 break;
265 case JSTYPE_STRING:
266 case JSTYPE_OBJECT:
268 JSString *str = ::JS_ValueToString(cx, argv[0]);
269 if (!str)
270 return NS_ERROR_OUT_OF_MEMORY;
272 expr = ::JS_FlattenString(cx, str);
273 if (!expr)
274 return NS_ERROR_OUT_OF_MEMORY;
276 argv[0] = STRING_TO_JSVAL(str);
278 break;
280 default:
281 ::JS_ReportError(cx, "useless %s call (missing quotes around argument?)",
282 *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
284 // Return an error that nsGlobalWindow can recognize and turn into NS_OK.
285 return NS_ERROR_DOM_TYPE_ERR;
288 if (expr) {
289 // if CSP is enabled, and setTimeout/setInterval was called with a string
290 // or object, disable the registration and log an error
291 nsCOMPtr<nsIDocument> doc = do_QueryInterface(aWindow->GetExtantDocument());
293 if (doc) {
294 nsCOMPtr<nsIContentSecurityPolicy> csp;
295 nsresult rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
296 NS_ENSURE_SUCCESS(rv, rv);
298 if (csp) {
299 PRBool allowsEval;
300 // this call will send violation reports as warranted (and return true if
301 // reportOnly is set).
302 rv = csp->GetAllowsEval(&allowsEval);
303 NS_ENSURE_SUCCESS(rv, rv);
305 if (!allowsEval) {
306 ::JS_ReportError(cx, "call to %s blocked by CSP",
307 *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
309 // Note: Our only caller knows to turn NS_ERROR_DOM_TYPE_ERR into NS_OK.
310 return NS_ERROR_DOM_TYPE_ERR;
313 } // if there's no document, we don't have to do anything.
315 rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
316 NS_ENSURE_SUCCESS(rv, rv);
318 mExpr = expr;
320 nsIPrincipal *prin = aWindow->GetPrincipal();
322 // Get the calling location.
323 const char *filename;
324 if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo, prin)) {
325 mFileName.Assign(filename);
327 } else if (funobj) {
328 rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
329 NS_ENSURE_SUCCESS(rv, rv);
331 mFunObj = funobj;
333 // Create our arg array - leave an extra slot for a secret final argument
334 // that indicates to the called function how "late" the timeout is. We
335 // will fill that in when SetLateness is called.
336 nsCOMPtr<nsIArray> array;
337 rv = NS_CreateJSArgv(cx, (argc > 1) ? argc - 1 : argc, nsnull,
338 getter_AddRefs(array));
339 if (NS_FAILED(rv)) {
340 return NS_ERROR_OUT_OF_MEMORY;
343 PRUint32 dummy;
344 jsval *jsargv = nsnull;
345 nsCOMPtr<nsIJSArgArray> jsarray(do_QueryInterface(array));
346 jsarray->GetArgs(&dummy, reinterpret_cast<void **>(&jsargv));
348 // must have worked - we own the impl! :)
349 NS_ASSERTION(jsargv, "No argv!");
350 for (PRInt32 i = 2; (PRUint32)i < argc; ++i) {
351 jsargv[i - 2] = argv[i];
353 // final arg slot remains null, array has rooted vals.
354 mArgv = array;
355 } else {
356 NS_WARNING("No func and no expr - why are we here?");
358 *aInterval = interval;
359 return NS_OK;
362 void nsJSScriptTimeoutHandler::SetLateness(PRIntervalTime aHowLate)
364 nsCOMPtr<nsIJSArgArray> jsarray(do_QueryInterface(mArgv));
365 if (jsarray) {
366 PRUint32 argc;
367 jsval *jsargv;
368 nsresult rv = jsarray->GetArgs(&argc, reinterpret_cast<void **>(&jsargv));
369 if (NS_SUCCEEDED(rv) && jsargv && argc)
370 jsargv[argc-1] = INT_TO_JSVAL((jsint) aHowLate);
371 } else {
372 NS_ERROR("How can our argv not handle this?");
376 const PRUnichar *
377 nsJSScriptTimeoutHandler::GetHandlerText()
379 NS_ASSERTION(mExpr, "No expression, so no handler text!");
380 return ::JS_GetFlatStringChars(mExpr);
383 nsresult NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
384 PRBool *aIsInterval,
385 PRInt32 *aInterval,
386 nsIScriptTimeoutHandler **aRet)
388 *aRet = nsnull;
389 nsJSScriptTimeoutHandler *handler = new nsJSScriptTimeoutHandler();
390 if (!handler)
391 return NS_ERROR_OUT_OF_MEMORY;
393 nsresult rv = handler->Init(aWindow, aIsInterval, aInterval);
394 if (NS_FAILED(rv)) {
395 delete handler;
396 return rv;
399 NS_ADDREF(*aRet = handler);
401 return NS_OK;