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"
53 #include "nsIContentSecurityPolicy.h"
55 static const char kSetIntervalStr
[] = "setInterval";
56 static const char kSetTimeoutStr
[] = "setTimeout";
58 // Our JS nsIScriptTimeoutHandler implementation.
59 class nsJSScriptTimeoutHandler
: public nsIScriptTimeoutHandler
63 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
64 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsJSScriptTimeoutHandler
)
66 nsJSScriptTimeoutHandler();
67 ~nsJSScriptTimeoutHandler();
69 virtual const PRUnichar
*GetHandlerText();
70 virtual void *GetScriptObject() {
73 virtual void GetLocation(const char **aFileName
, PRUint32
*aLineNo
) {
74 *aFileName
= mFileName
.get();
78 virtual PRUint32
GetScriptTypeID() {
79 return nsIProgrammingLanguage::JAVASCRIPT
;
81 virtual PRUint32
GetScriptVersion() {
85 virtual nsIArray
*GetArgv() {
88 // Called by the timeout mechanism so the secret 'lateness' arg can be
90 virtual void SetLateness(PRIntervalTime aHowLate
);
92 nsresult
Init(nsGlobalWindow
*aWindow
, PRBool
*aIsInterval
,
95 void ReleaseJSObjects();
99 nsCOMPtr
<nsIScriptContext
> mContext
;
101 // filename, line number and JS language version string of the
102 // caller of setTimeout()
106 nsCOMPtr
<nsIArray
> mArgv
;
108 // The JS expression to evaluate or function to call, if !mExpr
114 // nsJSScriptTimeoutHandler
115 // QueryInterface implementation for nsJSScriptTimeoutHandler
116 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler
)
117 NS_IMPL_CYCLE_COLLECTION_ROOT_BEGIN(nsJSScriptTimeoutHandler
)
118 tmp
->ReleaseJSObjects();
119 NS_IMPL_CYCLE_COLLECTION_ROOT_END
120 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsJSScriptTimeoutHandler
)
121 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSScriptTimeoutHandler
)
122 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext
)
123 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgv
)
124 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
125 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
127 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler
)
128 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mExpr
)
129 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFunObj
)
130 NS_IMPL_CYCLE_COLLECTION_TRACE_END
132 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler
)
133 NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler
)
134 NS_INTERFACE_MAP_ENTRY(nsISupports
)
137 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler
)
138 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler
)
140 nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler() :
148 nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler()
154 nsJSScriptTimeoutHandler::ReleaseJSObjects()
156 if (mExpr
|| mFunObj
) {
158 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
160 } else if (mFunObj
) {
161 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
164 NS_WARNING("No func and no expr - roots may not have been removed");
170 nsJSScriptTimeoutHandler::Init(nsGlobalWindow
*aWindow
, PRBool
*aIsInterval
,
173 mContext
= aWindow
->GetContextInternal();
175 // This window was already closed, or never properly initialized,
176 // don't let a timer be scheduled on such a window.
178 return NS_ERROR_NOT_INITIALIZED
;
181 nsAXPCNativeCallContext
*ncc
= nsnull
;
182 nsresult rv
= nsContentUtils::XPConnect()->
183 GetCurrentNativeCallContext(&ncc
);
184 NS_ENSURE_SUCCESS(rv
, rv
);
187 return NS_ERROR_NOT_AVAILABLE
;
189 JSContext
*cx
= nsnull
;
191 rv
= ncc
->GetJSContext(&cx
);
192 NS_ENSURE_SUCCESS(rv
, rv
);
195 jsval
*argv
= nsnull
;
198 ncc
->GetArgvPtr(&argv
);
200 JSString
*expr
= nsnull
;
201 JSObject
*funobj
= nsnull
;
204 JSAutoRequest
ar(cx
);
207 ::JS_ReportError(cx
, "Function %s requires at least 2 parameter",
208 *aIsInterval
? kSetIntervalStr
: kSetTimeoutStr
);
209 return NS_ERROR_DOM_TYPE_ERR
;
212 if (argc
> 1 && !::JS_ValueToECMAInt32(cx
, argv
[1], &interval
)) {
214 "Second argument to %s must be a millisecond interval",
215 aIsInterval
? kSetIntervalStr
: kSetTimeoutStr
);
216 return NS_ERROR_DOM_TYPE_ERR
;
220 // If no interval was specified, treat this like a timeout, to avoid
221 // setting an interval of 0 milliseconds.
222 *aIsInterval
= PR_FALSE
;
225 switch (::JS_TypeOfValue(cx
, argv
[0])) {
226 case JSTYPE_FUNCTION
:
227 funobj
= JSVAL_TO_OBJECT(argv
[0]);
232 expr
= ::JS_ValueToString(cx
, argv
[0]);
234 return NS_ERROR_OUT_OF_MEMORY
;
235 argv
[0] = STRING_TO_JSVAL(expr
);
239 ::JS_ReportError(cx
, "useless %s call (missing quotes around argument?)",
240 *aIsInterval
? kSetIntervalStr
: kSetTimeoutStr
);
242 // Return an error that nsGlobalWindow can recognize and turn into NS_OK.
243 return NS_ERROR_DOM_TYPE_ERR
;
247 // if CSP is enabled, and setTimeout/setInterval was called with a string
248 // or object, disable the registration and log an error
249 nsCOMPtr
<nsIDocument
> doc
= do_QueryInterface(aWindow
->GetExtantDocument());
252 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
253 nsresult rv
= doc
->NodePrincipal()->GetCsp(getter_AddRefs(csp
));
254 NS_ENSURE_SUCCESS(rv
, rv
);
258 // this call will send violation reports as warranted (and return true if
259 // reportOnly is set).
260 rv
= csp
->GetAllowsEval(&allowsEval
);
261 NS_ENSURE_SUCCESS(rv
, rv
);
264 ::JS_ReportError(cx
, "call to %s blocked by CSP",
265 *aIsInterval
? kSetIntervalStr
: kSetTimeoutStr
);
267 // Note: Our only caller knows to turn NS_ERROR_DOM_TYPE_ERR into NS_OK.
268 return NS_ERROR_DOM_TYPE_ERR
;
271 } // if there's no document, we don't have to do anything.
273 rv
= NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
274 NS_ENSURE_SUCCESS(rv
, rv
);
278 nsIPrincipal
*prin
= aWindow
->GetPrincipal();
280 // Get the calling location.
281 const char *filename
;
282 if (nsJSUtils::GetCallingLocation(cx
, &filename
, &mLineNo
, prin
)) {
283 mFileName
.Assign(filename
);
286 rv
= NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
287 NS_ENSURE_SUCCESS(rv
, rv
);
291 // Create our arg array - leave an extra slot for a secret final argument
292 // that indicates to the called function how "late" the timeout is. We
293 // will fill that in when SetLateness is called.
294 nsCOMPtr
<nsIArray
> array
;
295 rv
= NS_CreateJSArgv(cx
, (argc
> 1) ? argc
- 1 : argc
, nsnull
,
296 getter_AddRefs(array
));
298 return NS_ERROR_OUT_OF_MEMORY
;
302 jsval
*jsargv
= nsnull
;
303 nsCOMPtr
<nsIJSArgArray
> jsarray(do_QueryInterface(array
));
304 jsarray
->GetArgs(&dummy
, reinterpret_cast<void **>(&jsargv
));
306 // must have worked - we own the impl! :)
307 NS_ASSERTION(jsargv
, "No argv!");
308 for (PRInt32 i
= 2; (PRUint32
)i
< argc
; ++i
) {
309 jsargv
[i
- 2] = argv
[i
];
311 // final arg slot remains null, array has rooted vals.
314 NS_WARNING("No func and no expr - why are we here?");
316 *aInterval
= interval
;
320 void nsJSScriptTimeoutHandler::SetLateness(PRIntervalTime aHowLate
)
322 nsCOMPtr
<nsIJSArgArray
> jsarray(do_QueryInterface(mArgv
));
326 nsresult rv
= jsarray
->GetArgs(&argc
, reinterpret_cast<void **>(&jsargv
));
327 if (NS_SUCCEEDED(rv
) && jsargv
&& argc
)
328 jsargv
[argc
-1] = INT_TO_JSVAL((jsint
) aHowLate
);
330 NS_ERROR("How can our argv not handle this?");
335 nsJSScriptTimeoutHandler::GetHandlerText()
337 NS_ASSERTION(mExpr
, "No expression, so no handler text!");
338 return reinterpret_cast<const PRUnichar
*>
339 (::JS_GetStringChars(mExpr
));
342 nsresult
NS_CreateJSTimeoutHandler(nsGlobalWindow
*aWindow
,
345 nsIScriptTimeoutHandler
**aRet
)
348 nsJSScriptTimeoutHandler
*handler
= new nsJSScriptTimeoutHandler();
350 return NS_ERROR_OUT_OF_MEMORY
;
352 nsresult rv
= handler
->Init(aWindow
, aIsInterval
, aInterval
);
358 NS_ADDREF(*aRet
= handler
);