Bug 426991 - Form submission extremely slow on large forms (with form Form history...
[wine-gecko.git] / extensions / jssh / nsJSSh.cpp
blobc05d2bb6fb72814e6792ff884d2a449e112fc95f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is the Mozilla JavaScript Shell project.
17 * The Initial Developer of the Original Code is
18 * Alex Fritze.
19 * Portions created by the Initial Developer are Copyright (C) 2003
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Alex Fritze <alex@croczilla.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsJSSh.h"
40 #include "nsIJSRuntimeService.h"
41 #include "nsIServiceManager.h"
42 #include "nsIXPConnect.h"
43 #include "nsIProxyObjectManager.h"
44 #include "nsIScriptSecurityManager.h"
45 #include "nsIIOService.h"
46 #include "nsNetCID.h"
47 #include "nsIChannel.h"
48 #include "nsThreadUtils.h"
49 #include "nsServiceManagerUtils.h"
50 #include "nsXPCOMCIDInternal.h"
51 #include "nsMemory.h"
52 #include "nsAutoPtr.h"
54 #ifdef PR_LOGGING
55 PRLogModuleInfo *gJSShLog = PR_NewLogModule("jssh");
56 #endif
58 //**********************************************************************
59 // Javascript Environment
61 const char *gWelcome = "Welcome to the Mozilla JavaScript Shell!\n";
62 const char *gGoodbye = "Goodbye!\n";
64 // GetJSShGlobal: helper for native js functions to obtain the global
65 // JSSh object
66 PRBool GetJSShGlobal(JSContext *cx, JSObject *obj, nsJSSh** shell)
68 JSAutoRequest ar(cx);
70 #ifdef DEBUG
71 // printf("GetJSShGlobal(cx=%p, obj=%p)\n", cx, obj);
72 #endif
73 JSObject *parent, *global = obj;
74 if (!global) {
75 NS_ERROR("need a non-null obj to find script global");
76 return PR_FALSE;
79 // in most of our cases, 'global' is already the correct obj, since
80 // we use bound methods. For cases where we call
81 // GetJSShGlobal from an unbound method, we need to walk the
82 // parent chain:
83 // XXX I think the comment above is obsolete. We only call
84 // GetJSShGlobal from bound methods, in which case the
85 // parent is the global obj. For non-bound methods, walking the
86 // parent chain to obtain the global object will only work when the
87 // method is rooted in the current script context, so it is not
88 // reliable (???). ASSERTION below to test the assumption. Once proven,
89 // remove loop.
90 while ((parent = JS_GetParent(cx, global))) {
91 NS_ERROR("Parent chain weirdness. Probably benign, but we should not reach this.");
92 #ifdef DEBUG
93 // printf(" obj's parent = %p\n", parent);
94 #endif
95 global = parent;
98 // JSClass* clazz = JS_GET_CLASS(cx, global);
99 // if (!IS_WRAPPER_CLASS(clazz)) {
100 // NS_ERROR("the script global's class is not of the right type");
101 // return PR_FALSE;
102 // }
104 // XXX use GetWrappedNativeOfJSObject
105 nsIXPConnectWrappedNative *wrapper = static_cast<nsIXPConnectWrappedNative*>(JS_GetPrivate(cx, global));
106 nsCOMPtr<nsISupports> native;
107 wrapper->GetNative(getter_AddRefs(native));
108 nsCOMPtr<nsIJSSh> jssh = do_QueryInterface(native);
109 NS_ASSERTION(jssh, "no jssh global");
110 *shell = static_cast<nsJSSh*>((nsIJSSh*)(jssh.get()));
111 return PR_TRUE;
114 static void
115 my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
117 // xxx getting the global obj from the cx. will that give us grief?
118 JSObject* obj = JS_GetGlobalObject(cx);
119 nsJSSh* shell;
120 if (!GetJSShGlobal(cx, obj, &shell)) return;
122 // XXX use JSErrorReport for better info
124 PRUint32 bytesWritten;
125 if (shell->mOutput) {
126 if (shell->mEmitHeader) {
127 char buf[80];
128 sprintf(buf, "[%d]", strlen(message));
129 shell->mOutput->Write(buf, strlen(buf), &bytesWritten);
131 shell->mOutput->Write(message, strlen(message), &bytesWritten);
135 static JSBool
136 Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
138 nsJSSh* shell;
139 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
141 JSAutoRequest ar(cx);
143 PRUint32 bytesWritten;
145 #ifdef DEBUG
146 // nsCOMPtr<nsIThread> thread;
147 // nsIThread::GetCurrent(getter_AddRefs(thread));
148 // printf("printing on thread %p, shell %p, output %p, cx=%p, obj=%p\n", thread.get(), shell, shell->mOutput, cx, obj);
149 #endif
151 for (unsigned int i=0; i<argc; ++i) {
152 JSString *str = JS_ValueToString(cx, argv[i]);
153 if (!str) return JS_FALSE;
154 if (shell->mOutput) {
155 if (shell->mEmitHeader) {
156 char buf[80];
157 sprintf(buf, "[%d]", JS_GetStringLength(str));
158 shell->mOutput->Write(buf, strlen(buf), &bytesWritten);
160 shell->mOutput->Write(JS_GetStringBytes(str), JS_GetStringLength(str), &bytesWritten);
162 else
163 printf("%s", JS_GetStringBytes(str)); // use cout if no output stream given.
164 #ifdef DEBUG
165 // printf("%s", JS_GetStringBytes(str));
166 #endif
168 return JS_TRUE;
171 static JSBool
172 Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
174 nsJSSh* shell;
175 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
177 PRUint32 bytesWritten;
179 if (shell->mOutput)
180 shell->mOutput->Write(gGoodbye, strlen(gGoodbye), &bytesWritten);
181 shell->mQuit = PR_TRUE;
183 return JS_TRUE;
186 static JSBool
187 Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
189 nsJSSh* shell;
191 JSAutoRequest ar(cx);
193 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
195 for (unsigned int i=0; i<argc; ++i) {
196 JSString *str = JS_ValueToString(cx, argv[i]);
197 if (!str) return JS_FALSE;
198 //argv[i] = STRING_TO_JSVAL(str);
199 const char *url = JS_GetStringBytes(str);
200 if (!shell->LoadURL(url, rval))
201 return JS_FALSE;
203 return JS_TRUE;
206 static JSBool
207 FlushEventQueue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
209 nsJSSh* shell;
210 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
212 NS_ProcessPendingEvents(nsnull);
214 return JS_TRUE;
217 static JSBool
218 Suspend(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
220 nsJSSh* shell;
221 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
223 nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
225 PR_AtomicIncrement(&shell->mSuspendCount);
227 while (shell->mSuspendCount) {
228 LOG(("|"));
229 NS_ProcessNextEvent(thread);
232 return JS_TRUE;
235 static JSBool
236 Resume(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
238 nsJSSh* shell;
239 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
241 PR_AtomicDecrement(&shell->mSuspendCount);
243 return JS_TRUE;
246 static JSBool
247 AddressOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
249 if (argc!=1) return JS_FALSE;
251 JSAutoRequest ar(cx);
253 // xxx If argv[0] is not an obj already, we'll get a transient
254 // address from JS_ValueToObject. Maybe we should throw an exception
255 // instead.
257 JSObject *arg_obj;
258 if (!JS_ValueToObject(cx, argv[0], &arg_obj)) {
259 return JS_FALSE;
262 char buf[80];
263 sprintf(buf, "%p", arg_obj);
264 JSString *str = JS_NewStringCopyZ(cx, buf);
265 *rval = STRING_TO_JSVAL(str);
266 return JS_TRUE;
269 static JSBool
270 SetProtocol(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
272 if (argc!=1) return JS_FALSE;
273 nsJSSh* shell;
274 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
276 JSAutoRequest ar(cx);
278 JSString *str = JS_ValueToString(cx, argv[0]);
279 if (!str) return JS_FALSE;
280 char *protocol = JS_GetStringBytes(str);
282 if (!strcmp(protocol, "interactive")) {
283 shell->mEmitHeader = PR_FALSE;
284 shell->mPrompt = NS_LITERAL_CSTRING("\n> ");
285 shell->mProtocol = protocol;
287 else if (!strcmp(protocol, "synchronous")) {
288 shell->mEmitHeader = PR_TRUE;
289 shell->mPrompt = NS_LITERAL_CSTRING("\n> ");
290 shell->mProtocol = protocol;
292 else if (!strcmp(protocol, "plain")) {
293 shell->mEmitHeader = PR_FALSE;
294 shell->mPrompt = NS_LITERAL_CSTRING("\n");
295 shell->mProtocol = protocol;
297 else return JS_FALSE;
299 return JS_TRUE;
302 static JSBool
303 GetProtocol(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
305 nsJSSh* shell;
306 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
308 JSAutoRequest ar(cx);
310 JSString *str = JS_NewStringCopyZ(cx, shell->mProtocol.get());
311 *rval = STRING_TO_JSVAL(str);
312 return JS_TRUE;
315 static JSBool
316 SetContextObj(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
318 nsJSSh* shell;
319 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
321 JSAutoRequest ar(cx);
323 if (argc!=1) return JS_FALSE;
325 JSObject *arg_obj;
326 if (!JS_ValueToObject(cx, argv[0], &arg_obj)) {
327 return JS_FALSE;
330 if (shell->mContextObj != shell->mGlobal)
331 JS_RemoveRoot(cx, &(shell->mContextObj));
333 shell->mContextObj = arg_obj;
335 if (shell->mContextObj != shell->mGlobal)
336 JS_AddRoot(cx, &(shell->mContextObj));
338 return JS_TRUE;
341 static JSBool
342 DebugBreak(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
344 nsJSSh* shell;
345 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
347 NS_BREAK();
349 return JS_TRUE;
352 static JSBool
353 GetInputStream(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
355 nsJSSh* shell;
356 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
358 JSAutoRequest ar(cx);
360 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
361 if (!xpc) {
362 NS_ERROR("failed to get xpconnect service");
363 return JS_FALSE;
366 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
367 xpc->WrapNative(cx, shell->mGlobal, shell->mInput,
368 NS_GET_IID(nsIInputStream),
369 getter_AddRefs(wrapper));
370 if (!wrapper) {
371 NS_ERROR("could not wrap input stream object");
372 return JS_FALSE;
375 JSObject* wrapper_jsobj = nsnull;
376 wrapper->GetJSObject(&wrapper_jsobj);
377 NS_ASSERTION(wrapper_jsobj, "could not get jsobject of wrapped native");
379 *rval = OBJECT_TO_JSVAL(wrapper_jsobj);
381 return JS_TRUE;
384 static JSBool
385 GetOutputStream(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
387 nsJSSh* shell;
388 if (!GetJSShGlobal(cx, obj, &shell)) return JS_FALSE;
390 JSAutoRequest ar(cx);
392 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
393 if (!xpc) {
394 NS_ERROR("failed to get xpconnect service");
395 return JS_FALSE;
398 nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
399 xpc->WrapNative(cx, shell->mGlobal, shell->mOutput,
400 NS_GET_IID(nsIOutputStream),
401 getter_AddRefs(wrapper));
402 if (!wrapper) {
403 NS_ERROR("could not wrap output stream object");
404 return JS_FALSE;
407 JSObject* wrapper_jsobj = nsnull;
408 wrapper->GetJSObject(&wrapper_jsobj);
409 NS_ASSERTION(wrapper_jsobj, "could not get jsobject of wrapped native");
411 *rval = OBJECT_TO_JSVAL(wrapper_jsobj);
413 return JS_TRUE;
416 // these all need JSFUN_BOUND_METHOD flags, so that we can do
417 // things like:
418 // win.p = print, where win is rooted in some other global
419 // object.
420 static JSFunctionSpec global_functions[] = {
421 {"print", Print, 1, JSFUN_BOUND_METHOD, 0},
422 {"dump", Print, 1, JSFUN_BOUND_METHOD, 0},
423 {"quit", Quit, 0, JSFUN_BOUND_METHOD, 0},
424 {"exit", Quit, 0, JSFUN_BOUND_METHOD, 0},
425 {"load", Load, 1, JSFUN_BOUND_METHOD, 0},
426 {"suspend", Suspend, 0, JSFUN_BOUND_METHOD, 0},
427 {"resume", Resume, 0, JSFUN_BOUND_METHOD, 0},
428 {"flushEventQueue", FlushEventQueue,0, JSFUN_BOUND_METHOD, 0},
429 {"addressOf", AddressOf, 1, 0, 0},
430 {"setProtocol", SetProtocol, 1, JSFUN_BOUND_METHOD, 0},
431 {"getProtocol", GetProtocol, 0, JSFUN_BOUND_METHOD, 0},
432 {"setContextObj", SetContextObj, 1, JSFUN_BOUND_METHOD, 0},
433 {"debugBreak", DebugBreak, 0, JSFUN_BOUND_METHOD, 0},
434 {"getInputStream", GetInputStream, 0, JSFUN_BOUND_METHOD, 0},
435 {"getOutputStream", GetOutputStream,0, JSFUN_BOUND_METHOD, 0},
436 {nsnull, nsnull, 0, 0, 0}
440 //**********************************************************************
441 // nsJSSh Implementation
443 nsJSSh::nsJSSh(nsIInputStream* input,
444 nsIOutputStream*output,
445 const nsACString &startupURI) :
446 mInput(input), mOutput(output), mQuit(PR_FALSE), mStartupURI(startupURI),
447 mSuspendCount(0), mPrompt("\n> "),
448 mEmitHeader(PR_FALSE), mProtocol("interactive")
452 nsJSSh::~nsJSSh()
454 LOG(("JSSh: ~connection!\n"));
457 already_AddRefed<nsIRunnable>
458 CreateJSSh(nsIInputStream* input, nsIOutputStream*output,
459 const nsACString &startupURI)
461 nsIRunnable* obj = new nsJSSh(input, output, startupURI);
462 NS_IF_ADDREF(obj);
463 return obj;
466 //----------------------------------------------------------------------
467 // nsISupports methods:
469 NS_IMPL_THREADSAFE_ADDREF(nsJSSh)
470 NS_IMPL_THREADSAFE_RELEASE(nsJSSh)
472 NS_INTERFACE_MAP_BEGIN(nsJSSh)
473 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSSh)
474 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
475 NS_INTERFACE_MAP_ENTRY(nsIJSSh)
476 NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
477 NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
478 NS_INTERFACE_MAP_END
481 //----------------------------------------------------------------------
482 // nsIRunnable methods:
484 NS_IMETHODIMP nsJSSh::Run()
486 nsCOMPtr<nsIJSSh> proxied_shell;
487 if (!NS_IsMainThread()) {
488 nsCOMPtr<nsIProxyObjectManager> pom = do_GetService(NS_XPCOMPROXY_CONTRACTID);
489 NS_ASSERTION(pom, "uh-oh, no proxy object manager!");
490 pom->GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
491 NS_GET_IID(nsIJSSh),
492 (nsIJSSh*)this,
493 NS_PROXY_SYNC,
494 getter_AddRefs(proxied_shell));
496 else {
497 LOG(("jssh shell will block main thread!\n"));
498 proxied_shell = this;
500 proxied_shell->Init();
502 if (mInput) {
503 // read-eval-print loop
504 PRUint32 bytesWritten;
505 if (mOutput && !mProtocol.Equals(NS_LITERAL_CSTRING("plain")))
506 mOutput->Write(gWelcome, strlen(gWelcome), &bytesWritten);
508 while (!mQuit) {
509 if (mOutput)
510 mOutput->Write(mPrompt.get(), mPrompt.Length(), &bytesWritten);
512 // accumulate input until we get a compilable unit:
513 PRBool iscompilable;
514 mBufferPtr = 0;
515 #ifdef DEBUG
516 // nsCOMPtr<nsIThread> thread;
517 // nsIThread::GetCurrent(getter_AddRefs(thread));
518 // printf("blocking on thread %p\n", thread.get());
519 #endif
520 do {
521 PRUint32 bytesRead = 0;
522 mInput->Read(mBuffer+mBufferPtr, 1, &bytesRead);
523 if (bytesRead) {
524 ++mBufferPtr;
526 else {
527 // connection was terminated by the client
528 mQuit = PR_TRUE;
529 break;
531 // XXX signal buffer overflow ??
532 // XXX ideally we want a dynamically resizing buffer.
533 }while (mBufferPtr<cBufferSize &&
534 (mBuffer[mBufferPtr-1]!=10 || (NS_SUCCEEDED(proxied_shell->IsBufferCompilable(&iscompilable)) && !iscompilable)));
535 NS_ASSERTION(mBufferPtr<cBufferSize, "buffer overflow");
537 proxied_shell->ExecuteBuffer();
541 proxied_shell->Cleanup();
543 if (!NS_IsMainThread()) {
544 // Shutdown the current thread, which must be done from the main thread.
545 nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
546 nsCOMPtr<nsIThread> proxied_thread;
547 nsCOMPtr<nsIProxyObjectManager> pom = do_GetService(NS_XPCOMPROXY_CONTRACTID);
548 NS_ASSERTION(pom, "uh-oh, no proxy object manager!");
549 pom->GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
550 NS_GET_IID(nsIThread),
551 thread.get(),
552 NS_PROXY_ASYNC,
553 getter_AddRefs(proxied_thread));
554 if (proxied_thread)
555 proxied_thread->Shutdown();
557 return NS_OK;
560 //----------------------------------------------------------------------
561 // nsIJSSh methods
563 NS_IMETHODIMP nsJSSh::Init()
565 nsCOMPtr<nsIScriptSecurityManager> ssm = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
566 if (!ssm) {
567 NS_ERROR("failed to get script security manager");
568 return NS_ERROR_FAILURE;
571 ssm->GetSystemPrincipal(getter_AddRefs(mPrincipal));
572 if (!mPrincipal) {
573 NS_ERROR("failed to get system principal");
574 return NS_ERROR_FAILURE;
577 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
578 if (!xpc) {
579 NS_ERROR("failed to get xpconnect service");
580 return NS_ERROR_FAILURE;
583 // Let xpconnect resync its JSContext tracker. We do this before creating
584 // a new JSContext just in case the heap manager recycles the JSContext
585 // struct.
586 xpc->SyncJSContexts();
588 nsCOMPtr<nsIJSRuntimeService> rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
589 // get the JSRuntime from the runtime svc
590 if (!rtsvc) {
591 NS_ERROR("failed to get nsJSRuntimeService");
592 return NS_ERROR_FAILURE;
595 JSRuntime *rt = nsnull;
596 if (NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
597 NS_ERROR("failed to get JSRuntime from nsJSRuntimeService");
598 return NS_ERROR_FAILURE;
601 mJSContext = JS_NewContext(rt, 8192);
602 if (!mJSContext) {
603 NS_ERROR("JS_NewContext failed");
604 return NS_ERROR_FAILURE;
607 JSAutoRequest ar(mJSContext);
609 // Enable e4x:
610 JS_SetOptions(mJSContext, JS_GetOptions(mJSContext) | JSOPTION_XML);
612 // Always use the latest js version
613 JS_SetVersion(mJSContext, JSVERSION_LATEST);
615 mContextStack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
616 if (!mContextStack) {
617 NS_ERROR("failed to get the nsThreadJSContextStack service");
618 return NS_ERROR_FAILURE;
621 JS_SetErrorReporter(mJSContext, my_ErrorReporter);
623 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
624 xpc->InitClassesWithNewWrappedGlobal(mJSContext, (nsIJSSh*)this,
625 NS_GET_IID(nsISupports),
626 PR_TRUE,
627 getter_AddRefs(holder));
628 if (!holder) {
629 NS_ERROR("global initialization failed");
630 return NS_ERROR_FAILURE;
633 holder->GetJSObject(&mGlobal);
634 if (!mGlobal) {
635 NS_ERROR("bad global object");
636 return NS_ERROR_FAILURE;
638 mContextObj = mGlobal;
640 if (!JS_DefineFunctions(mJSContext, mGlobal, global_functions)) {
641 NS_ERROR("failed to initialise global functions");
642 return NS_ERROR_FAILURE;
645 if (!mStartupURI.IsEmpty())
646 LoadURL(mStartupURI.get());
648 return NS_OK;
651 NS_IMETHODIMP nsJSSh::Cleanup()
653 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
654 if (!xpc) {
655 NS_ERROR("failed to get xpconnect service");
656 return NS_ERROR_FAILURE;
660 JSAutoRequest ar(mJSContext);
662 if (mContextObj != mGlobal)
663 JS_RemoveRoot(mJSContext, &(mContextObj));
665 JS_ClearScope(mJSContext, mGlobal);
666 JS_GC(mJSContext);
669 JS_DestroyContext(mJSContext);
670 xpc->SyncJSContexts();
671 return NS_OK;
674 NS_IMETHODIMP nsJSSh::ExecuteBuffer()
676 #ifdef DEBUG
677 // nsCOMPtr<nsIThread> thread;
678 // nsIThread::GetCurrent(getter_AddRefs(thread));
679 // printf("executing on thread %p\n", thread.get());
680 #endif
682 JS_BeginRequest(mJSContext);
683 JS_ClearPendingException(mJSContext);
684 JSPrincipals *jsprincipals;
685 mPrincipal->GetJSPrincipals(mJSContext, &jsprincipals);
687 if(NS_FAILED(mContextStack->Push(mJSContext))) {
688 NS_ERROR("failed to push the current JSContext on the nsThreadJSContextStack");
689 return NS_ERROR_FAILURE;
692 JSScript *script = JS_CompileScriptForPrincipals(mJSContext, mContextObj, jsprincipals, mBuffer, mBufferPtr, "interactive", 0);
694 if (script) {
695 jsval result;
696 if (JS_ExecuteScript(mJSContext, mContextObj, script, &result) && result!=JSVAL_VOID && mOutput) {
697 // XXX for some wrapped native objects the following code will crash; probably because the
698 // native object is getting released before we reach this:
699 JSString *str = JS_ValueToString(mJSContext, result);
700 if (str) {
701 nsDependentString chars(reinterpret_cast<const PRUnichar*>
702 (JS_GetStringChars(str)),
703 JS_GetStringLength(str));
704 NS_ConvertUTF16toUTF8 cstr(chars);
705 PRUint32 bytesWritten;
706 mOutput->Write(cstr.get(), cstr.Length(), &bytesWritten);
709 JS_DestroyScript(mJSContext, script);
712 JSContext *oldcx;
713 mContextStack->Pop(&oldcx);
714 NS_ASSERTION(oldcx == mJSContext, "JS thread context push/pop mismatch");
716 JSPRINCIPALS_DROP(mJSContext, jsprincipals);
718 JS_EndRequest(mJSContext);
720 return NS_OK;
723 NS_IMETHODIMP nsJSSh::IsBufferCompilable(PRBool *_retval)
725 JSAutoRequest ar(mJSContext);
726 *_retval = JS_BufferIsCompilableUnit(mJSContext, mContextObj, mBuffer, mBufferPtr);
727 return NS_OK;
730 //----------------------------------------------------------------------
731 // nsIScriptObjectPrincipal methods:
733 nsIPrincipal *
734 nsJSSh::GetPrincipal()
736 return mPrincipal;
739 //----------------------------------------------------------------------
740 // nsIXPCScriptable methods:
742 #define XPC_MAP_CLASSNAME nsJSSh
743 #define XPC_MAP_QUOTED_CLASSNAME "JSSh"
744 #define XPC_MAP_WANT_NEWRESOLVE
745 #define XPC_MAP_FLAGS nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY | \
746 nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY | \
747 nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY | \
748 nsIXPCScriptable::DONT_ENUM_STATIC_PROPS | \
749 nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \
750 nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
751 #include "xpc_map_end.h" /* This will #undef the above */
753 /* PRBool newResolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx,
754 in JSObjectPtr obj, in JSVal id, in PRUint32 flags, out JSObjectPtr objp); */
755 NS_IMETHODIMP
756 nsJSSh::NewResolve(nsIXPConnectWrappedNative *wrapper,
757 JSContext * cx, JSObject * obj,
758 jsval id, PRUint32 flags,
759 JSObject * *objp, PRBool *_retval)
761 JSBool resolved;
763 JSAutoRequest ar(cx);
765 *_retval = JS_ResolveStandardClass(cx, obj, id, &resolved);
766 if (*_retval && resolved)
767 *objp = obj;
768 return NS_OK;
771 //----------------------------------------------------------------------
772 // Implementation helpers:
774 PRBool nsJSSh::LoadURL(const char *url, jsval* retval)
776 nsCOMPtr<nsIIOService> ioserv = do_GetService(NS_IOSERVICE_CONTRACTID);
777 if (!ioserv) {
778 NS_ERROR("could not get io service");
779 return PR_FALSE;
782 nsCOMPtr<nsIChannel> channel;
783 ioserv->NewChannel(nsDependentCString(url), nsnull, nsnull, getter_AddRefs(channel));
784 if (!channel) {
785 NS_ERROR("could not create channel");
786 return PR_FALSE;
789 nsCOMPtr<nsIInputStream> instream;
790 channel->Open(getter_AddRefs(instream));
791 if (!instream) {
792 NS_ERROR("could not open stream");
793 return PR_FALSE;
796 nsCString buffer;
797 nsAutoArrayPtr<char> buf(new char[1024]);
798 if (!buf) {
799 NS_ERROR("could not alloc buffer");
800 return PR_FALSE;
803 PRUint32 bytesRead = 0;
805 do {
806 if (NS_FAILED(instream->Read(buf, 1024, &bytesRead))) {
807 NS_ERROR("stream read error");
808 return PR_FALSE;
810 buffer.Append(buf, bytesRead);
811 LOG(("appended %d bytes:\n%s", bytesRead, buffer.get()));
812 } while (bytesRead > 0);
814 LOG(("loaded %d bytes:\n%s", buffer.Length(), buffer.get()));
816 JS_BeginRequest(mJSContext);
817 JSPrincipals *jsprincipals;
818 mPrincipal->GetJSPrincipals(mJSContext, &jsprincipals);
820 if(NS_FAILED(mContextStack->Push(mJSContext))) {
821 NS_ERROR("failed to push the current JSContext on the nsThreadJSContextStack");
822 return NS_ERROR_FAILURE;
825 jsval result;
826 JSBool ok = JS_EvaluateScriptForPrincipals(mJSContext, mContextObj,
827 jsprincipals, buffer.get(),
828 buffer.Length(),
829 url, 1, &result);
830 JSPRINCIPALS_DROP(mJSContext, jsprincipals);
832 JSContext *oldcx;
833 mContextStack->Pop(&oldcx);
834 NS_ASSERTION(oldcx == mJSContext, "JS thread context push/pop mismatch");
836 if (ok && retval) *retval=result;
838 JS_EndRequest(mJSContext);
840 return ok;