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"
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
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() {
76 virtual void GetLocation(const char **aFileName
, PRUint32
*aLineNo
) {
77 *aFileName
= mFileName
.get();
81 virtual PRUint32
GetScriptTypeID() {
82 return nsIProgrammingLanguage::JAVASCRIPT
;
84 virtual PRUint32
GetScriptVersion() {
88 virtual nsIArray
*GetArgv() {
91 // Called by the timeout mechanism so the secret 'lateness' arg can be
93 virtual void SetLateness(PRIntervalTime aHowLate
);
95 nsresult
Init(nsGlobalWindow
*aWindow
, PRBool
*aIsInterval
,
98 void ReleaseJSObjects();
102 nsCOMPtr
<nsIScriptContext
> mContext
;
104 // filename, line number and JS language version string of the
105 // caller of setTimeout()
109 nsCOMPtr
<nsIArray
> mArgv
;
111 // The JS expression to evaluate or function to call, if !mExpr
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");
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();
137 size_t size
= 1 + JS_PutEscapedFlatString(NULL
, 0, ATOM_TO_STRING(fun
->atom
), 0);
138 char *name
= new char[size
];
140 JS_PutEscapedFlatString(name
, size
, ATOM_TO_STRING(fun
->atom
), 0);
141 foo
.AppendLiteral(" [");
144 foo
.AppendLiteral("]");
148 cb
.DescribeNode(RefCounted
, tmp
->mRefCnt
.get(),
149 sizeof(nsJSScriptTimeoutHandler
), foo
.get());
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
)
172 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler
)
173 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler
)
175 nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler() :
183 nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler()
189 nsJSScriptTimeoutHandler::ReleaseJSObjects()
191 if (mExpr
|| mFunObj
) {
193 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
195 } else if (mFunObj
) {
196 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
199 NS_WARNING("No func and no expr - roots may not have been removed");
205 nsJSScriptTimeoutHandler::Init(nsGlobalWindow
*aWindow
, PRBool
*aIsInterval
,
208 mContext
= aWindow
->GetContextInternal();
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
);
222 return NS_ERROR_NOT_AVAILABLE
;
224 JSContext
*cx
= nsnull
;
226 rv
= ncc
->GetJSContext(&cx
);
227 NS_ENSURE_SUCCESS(rv
, rv
);
230 jsval
*argv
= nsnull
;
233 ncc
->GetArgvPtr(&argv
);
235 JSFlatString
*expr
= nsnull
;
236 JSObject
*funobj
= nsnull
;
239 JSAutoRequest
ar(cx
);
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
)) {
249 "Second argument to %s must be a millisecond interval",
250 aIsInterval
? kSetIntervalStr
: kSetTimeoutStr
);
251 return NS_ERROR_DOM_TYPE_ERR
;
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]);
268 JSString
*str
= ::JS_ValueToString(cx
, argv
[0]);
270 return NS_ERROR_OUT_OF_MEMORY
;
272 expr
= ::JS_FlattenString(cx
, str
);
274 return NS_ERROR_OUT_OF_MEMORY
;
276 argv
[0] = STRING_TO_JSVAL(str
);
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
;
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());
294 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
295 nsresult rv
= doc
->NodePrincipal()->GetCsp(getter_AddRefs(csp
));
296 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
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
);
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
);
328 rv
= NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler
);
329 NS_ENSURE_SUCCESS(rv
, rv
);
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
));
340 return NS_ERROR_OUT_OF_MEMORY
;
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.
356 NS_WARNING("No func and no expr - why are we here?");
358 *aInterval
= interval
;
362 void nsJSScriptTimeoutHandler::SetLateness(PRIntervalTime aHowLate
)
364 nsCOMPtr
<nsIJSArgArray
> jsarray(do_QueryInterface(mArgv
));
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
);
372 NS_ERROR("How can our argv not handle this?");
377 nsJSScriptTimeoutHandler::GetHandlerText()
379 NS_ASSERTION(mExpr
, "No expression, so no handler text!");
380 return ::JS_GetFlatStringChars(mExpr
);
383 nsresult
NS_CreateJSTimeoutHandler(nsGlobalWindow
*aWindow
,
386 nsIScriptTimeoutHandler
**aRet
)
389 nsJSScriptTimeoutHandler
*handler
= new nsJSScriptTimeoutHandler();
391 return NS_ERROR_OUT_OF_MEMORY
;
393 nsresult rv
= handler
->Init(aWindow
, aIsInterval
, aInterval
);
399 NS_ADDREF(*aRet
= handler
);