Bug 513162 - Widget additions for recycling top level widgets as content containers...
[mozilla-central.git] / dom / base / nsJSTimeoutHandler.cpp
blob37e1833496d2b6984443bbb338731783e8a8c462
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 "nsIContentSecurityPolicy.h"
55 static const char kSetIntervalStr[] = "setInterval";
56 static const char kSetTimeoutStr[] = "setTimeout";
58 // Our JS nsIScriptTimeoutHandler implementation.
59 class nsJSScriptTimeoutHandler: public nsIScriptTimeoutHandler
61 public:
62 // nsISupports
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() {
71 return mFunObj;
73 virtual void GetLocation(const char **aFileName, PRUint32 *aLineNo) {
74 *aFileName = mFileName.get();
75 *aLineNo = mLineNo;
78 virtual PRUint32 GetScriptTypeID() {
79 return nsIProgrammingLanguage::JAVASCRIPT;
81 virtual PRUint32 GetScriptVersion() {
82 return mVersion;
85 virtual nsIArray *GetArgv() {
86 return mArgv;
88 // Called by the timeout mechanism so the secret 'lateness' arg can be
89 // added.
90 virtual void SetLateness(PRIntervalTime aHowLate);
92 nsresult Init(nsGlobalWindow *aWindow, PRBool *aIsInterval,
93 PRInt32 *aInterval);
95 void ReleaseJSObjects();
97 private:
99 nsCOMPtr<nsIScriptContext> mContext;
101 // filename, line number and JS language version string of the
102 // caller of setTimeout()
103 nsCString mFileName;
104 PRUint32 mLineNo;
105 PRUint32 mVersion;
106 nsCOMPtr<nsIArray> mArgv;
108 // The JS expression to evaluate or function to call, if !mExpr
109 JSString *mExpr;
110 JSObject *mFunObj;
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)
135 NS_INTERFACE_MAP_END
137 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsJSScriptTimeoutHandler)
138 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSScriptTimeoutHandler)
140 nsJSScriptTimeoutHandler::nsJSScriptTimeoutHandler() :
141 mLineNo(0),
142 mVersion(nsnull),
143 mExpr(nsnull),
144 mFunObj(nsnull)
148 nsJSScriptTimeoutHandler::~nsJSScriptTimeoutHandler()
150 ReleaseJSObjects();
153 void
154 nsJSScriptTimeoutHandler::ReleaseJSObjects()
156 if (mExpr || mFunObj) {
157 if (mExpr) {
158 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
159 mExpr = nsnull;
160 } else if (mFunObj) {
161 NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
162 mFunObj = nsnull;
163 } else {
164 NS_WARNING("No func and no expr - roots may not have been removed");
169 nsresult
170 nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, PRBool *aIsInterval,
171 PRInt32 *aInterval)
173 mContext = aWindow->GetContextInternal();
174 if (!mContext) {
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);
186 if (!ncc)
187 return NS_ERROR_NOT_AVAILABLE;
189 JSContext *cx = nsnull;
191 rv = ncc->GetJSContext(&cx);
192 NS_ENSURE_SUCCESS(rv, rv);
194 PRUint32 argc;
195 jsval *argv = nsnull;
197 ncc->GetArgc(&argc);
198 ncc->GetArgvPtr(&argv);
200 JSString *expr = nsnull;
201 JSObject *funobj = nsnull;
202 int32 interval = 0;
204 JSAutoRequest ar(cx);
206 if (argc < 1) {
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)) {
213 ::JS_ReportError(cx,
214 "Second argument to %s must be a millisecond interval",
215 aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
216 return NS_ERROR_DOM_TYPE_ERR;
219 if (argc == 1) {
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]);
228 break;
230 case JSTYPE_STRING:
231 case JSTYPE_OBJECT:
232 expr = ::JS_ValueToString(cx, argv[0]);
233 if (!expr)
234 return NS_ERROR_OUT_OF_MEMORY;
235 argv[0] = STRING_TO_JSVAL(expr);
236 break;
238 default:
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;
246 if (expr) {
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());
251 if (doc) {
252 nsCOMPtr<nsIContentSecurityPolicy> csp;
253 nsresult rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp));
254 NS_ENSURE_SUCCESS(rv, rv);
256 if (csp) {
257 PRBool allowsEval;
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);
263 if (!allowsEval) {
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);
276 mExpr = expr;
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);
285 } else if (funobj) {
286 rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
287 NS_ENSURE_SUCCESS(rv, rv);
289 mFunObj = funobj;
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));
297 if (NS_FAILED(rv)) {
298 return NS_ERROR_OUT_OF_MEMORY;
301 PRUint32 dummy;
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.
312 mArgv = array;
313 } else {
314 NS_WARNING("No func and no expr - why are we here?");
316 *aInterval = interval;
317 return NS_OK;
320 void nsJSScriptTimeoutHandler::SetLateness(PRIntervalTime aHowLate)
322 nsCOMPtr<nsIJSArgArray> jsarray(do_QueryInterface(mArgv));
323 if (jsarray) {
324 PRUint32 argc;
325 jsval *jsargv;
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);
329 } else {
330 NS_ERROR("How can our argv not handle this?");
334 const PRUnichar *
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,
343 PRBool *aIsInterval,
344 PRInt32 *aInterval,
345 nsIScriptTimeoutHandler **aRet)
347 *aRet = nsnull;
348 nsJSScriptTimeoutHandler *handler = new nsJSScriptTimeoutHandler();
349 if (!handler)
350 return NS_ERROR_OUT_OF_MEMORY;
352 nsresult rv = handler->Init(aWindow, aIsInterval, aInterval);
353 if (NS_FAILED(rv)) {
354 delete handler;
355 return rv;
358 NS_ADDREF(*aRet = handler);
360 return NS_OK;