author | Ben Turner <bent.mozilla@gmail.com> |
Sat, 11 Jul 2009 02:33:10 -0400 | |
changeset 35771 | ec2881eb7e06471bda1e5c16534cdb945bcaf34a |
parent 35770 | bbcef80246c85787753bc1ea6d62ef30d1c9ba7f |
child 35772 | 3b2c1dba74e27b6ae61004e7738c4e26e3257650 |
push id | unknown |
push user | unknown |
push date | unknown |
milestone | 1.9.2a1pre |
--- a/ipc/Makefile.in +++ b/ipc/Makefile.in @@ -36,12 +36,12 @@ DEPTH = .. topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -DIRS += chromium glue +DIRS += chromium glue testshell TOOL_DIRS = app include $(topsrcdir)/config/rules.mk
--- a/ipc/app/MozillaRuntimeMain.cpp +++ b/ipc/app/MozillaRuntimeMain.cpp @@ -47,26 +47,23 @@ #endif #ifdef XP_WIN #include <windows.h> // we want a wmain entry point #include "nsWindowsWMain.cpp" #endif -class ScopedLogging -{ -public: - ScopedLogging() { NS_LogInit(); } - ~ScopedLogging() { NS_LogTerm(); } -}; - int main(int argc, char* argv[]) { - ScopedLogging log; +#if 0 + MessageBox(NULL, L"Hi", L"Hi", MB_OK); +#endif GeckoChildProcessType proctype = - XRE_StringToChildProcessType(argv[argc-1]); - nsresult rv = XRE_InitChildProcess(--argc, argv, proctype); + XRE_StringToChildProcessType(argv[argc - 1]); + + nsresult rv = XRE_InitChildProcess(argc - 1, argv, proctype); NS_ENSURE_SUCCESS(rv, 1); + return 0; }
--- a/ipc/glue/GeckoChildProcessHost.h +++ b/ipc/glue/GeckoChildProcessHost.h @@ -56,16 +56,20 @@ public: bool Launch(std::vector<std::wstring> aExtraOpts=std::vector<std::wstring>()); // FIXME/cjones: these should probably disappear virtual void OnMessageReceived(const IPC::Message& aMsg); virtual void OnChannelError(); virtual bool CanShutdown() { return true; } + IPC::Channel* GetChannel() { + return channelp(); + } + protected: GeckoChildProcessType mProcessType; FilePath mProcessPath; #if defined(OS_POSIX) base::file_handle_mapping_vector mFileMap; #endif
--- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -46,16 +46,18 @@ // Used by chrome code, we override this here and #ifdef theirs out. See the // comments in chrome/common/ipc_message_utils.h for info about this enum. enum IPCMessageStart { NPAPIProtocolMsgStart = 0, NPPProtocolMsgStart = 0, IFrameEmbeddingProtocolMsgStart, + TestShellProtocolMsgStart, + LastMsgIndex }; COMPILE_ASSERT(LastMsgIndex <= 16, need_to_update_IPC_MESSAGE_MACRO); namespace IPC {
new file mode 100644 --- /dev/null +++ b/ipc/testshell/IPCMessageStart.h @@ -0,0 +1,9 @@ + +// CODE GENERATED by ipdl.py. Do not edit. + +enum IPCMessageStart { + + + LastMsgIndex +}; +
new file mode 100644 --- /dev/null +++ b/ipc/testshell/Makefile.in @@ -0,0 +1,75 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla IPCShell. +# +# The Initial Developer of the Original Code is +# Ben Turner <bent.mozilla@gmail.com>. +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = ipcshell +LIBRARY_NAME = ipcshell_s +FORCE_STATIC_LIB = 1 +LIBXUL_LIBRARY = 1 +EXPORT_LIBRARY = 1 + +EXPORTS_NAMESPACES = mozilla/ipc + +EXPORTS_mozilla/ipc = \ + TestShellChild.h \ + TestShellParent.h \ + TestShellProtocol.h \ + TestShellProtocolChild.h \ + TestShellProtocolParent.h \ + TestShellThread.h \ + XPCShellEnvironment.h \ + $(NULL) + +CPPSRCS += \ + TestShellChild.cpp \ + TestShellParent.cpp \ + TestShellThread.cpp \ + XPCShellEnvironment.cpp \ + $(NULL) + +LOCAL_INCLUDES += -I$(topsrcdir)/js/src/xpconnect/shell + +TOOL_DIRS += app + +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk +include $(topsrcdir)/config/rules.mk
new file mode 100644 --- /dev/null +++ b/ipc/testshell/TestShell.ipdl @@ -0,0 +1,47 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPCShell. + * + * The Initial Developer of the Original Code is + * Ben Turner <bent.mozilla@gmail.com>. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +namespace mozilla { +namespace ipc { + +rpc protocol TestShell +{ + child: + rpc SendCommand(String aCommand); +}; + +} // namespace ipc +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/ipc/testshell/TestShellChild.cpp @@ -0,0 +1,65 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPCShell. + * + * The Initial Developer of the Original Code is + * Ben Turner <bent.mozilla@gmail.com>. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "TestShellChild.h" + +#include "XPCShellEnvironment.h" + +using mozilla::ipc::TestShellChild; +using mozilla::ipc::XPCShellEnvironment; + +TestShellChild::TestShellChild() +: mXPCShell(nsnull) +{ + +} + +TestShellChild::~TestShellChild() +{ + +} + +nsresult +TestShellChild::AnswerSendCommand(const String& aCommand) +{ + nsresult rv = mXPCShell->EvaluateString(aCommand) ? NS_OK : NS_ERROR_FAILURE; + + if (mXPCShell->IsQuitting()) { + MessageLoop::current()->Quit(); + } + + return rv; +}
new file mode 100644 --- /dev/null +++ b/ipc/testshell/TestShellChild.h @@ -0,0 +1,68 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPCShell. + * + * The Initial Developer of the Original Code is + * Ben Turner <bent.mozilla@gmail.com>. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _IPC_TESTSHELL_TESTSHELLCHILD_H_ +#define _IPC_TESTSHELL_TESTSHELLCHILD_H_ + +#include "mozilla/ipc/TestShellProtocolChild.h" + +namespace mozilla { +namespace ipc { + +class XPCShellEnvironment; + +class TestShellChild : public TestShellProtocolChild +{ +public: + typedef mozilla::ipc::String String; + + TestShellChild(); + virtual ~TestShellChild(); + + virtual nsresult AnswerSendCommand(const String& aCommand); + + void SetXPCShell(XPCShellEnvironment* aXPCShell) { + mXPCShell = aXPCShell; + } + +private: + XPCShellEnvironment* mXPCShell; +}; + +} /* namespace ipc */ +} /* namespace mozilla */ + +#endif /* _IPC_TESTSHELL_TESTSHELLCHILD_H_ */
new file mode 100644 --- /dev/null +++ b/ipc/testshell/TestShellParent.cpp @@ -0,0 +1,49 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPCShell. + * + * The Initial Developer of the Original Code is + * Ben Turner <bent.mozilla@gmail.com>. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "TestShellParent.h" + +using mozilla::ipc::TestShellParent; + +TestShellParent::TestShellParent() +{ + +} + +TestShellParent::~TestShellParent() +{ + +}
new file mode 100644 --- /dev/null +++ b/ipc/testshell/TestShellParent.h @@ -0,0 +1,57 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPCShell. + * + * The Initial Developer of the Original Code is + * Ben Turner <bent.mozilla@gmail.com>. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _IPC_TESTSHELL_TESTSHELLPARENT_H_ +#define _IPC_TESTSHELL_TESTSHELLPARENT_H_ + +#include "mozilla/ipc/TestShellProtocolParent.h" + +namespace mozilla { +namespace ipc { + +class TestShellParent : public TestShellProtocolParent +{ +public: + typedef mozilla::ipc::String String; + + TestShellParent(); + ~TestShellParent(); +}; + +} /* namespace ipc */ +} /* namespace mozilla */ + +#endif /* _IPC_TESTSHELL_TESTSHELLPARENT_H_ */
new file mode 100644 --- /dev/null +++ b/ipc/testshell/TestShellProtocol.h @@ -0,0 +1,91 @@ +// +// Automatically generated by ipdlc. +// Edit at your own risk +// + +#ifndef mozilla_ipc_TestShellProtocol_h +#define mozilla_ipc_TestShellProtocol_h + +#include "base/basictypes.h" +#include "nscore.h" +#include "IPC/IPCMessageUtils.h" +#include "mozilla/ipc/MessageTypes.h" +#include "mozilla/ipc/ProtocolUtils.h" + +namespace mozilla { +namespace ipc { +namespace TestShellProtocol { + + +enum State { + StateStart = 0, + StateError, + StateLast +}; + +enum MessageType { + TestShellProtocolStart = TestShellProtocolMsgStart << 12, + TestShellProtocolPreStart = (TestShellProtocolMsgStart << 12) - 1, + Msg_SendCommand__ID, + Reply_SendCommand__ID, + TestShellProtocolEnd +}; + +class Msg_SendCommand : + public IPC::Message +{ +private: + typedef mozilla::ipc::String String; + typedef mozilla::ipc::StringArray StringArray; + +public: + enum { + ID = Msg_SendCommand__ID + }; + Msg_SendCommand(const String& aCommand) : + IPC::Message(MSG_ROUTING_NONE, ID, PRIORITY_NORMAL) + { + IPC::WriteParam(this, aCommand); + } + + static bool Read( + const Message* msg, + String* aCommand) + { + void* iter = 0; + + if (!(IPC::ReadParam(msg, &(iter), aCommand))) { + return false; + } + + return true; + } +}; +class Reply_SendCommand : + public IPC::Message +{ +private: + typedef mozilla::ipc::String String; + typedef mozilla::ipc::StringArray StringArray; + +public: + enum { + ID = Reply_SendCommand__ID + }; + Reply_SendCommand() : + IPC::Message(MSG_ROUTING_NONE, ID, PRIORITY_NORMAL) + { + } + + static bool Read(const Message* msg) + { + return true; + } +}; + + +} // namespace TestShellProtocol +} // namespace ipc +} // namespace mozilla + +#endif // ifndef mozilla_ipc_TestShellProtocol_h
new file mode 100644 --- /dev/null +++ b/ipc/testshell/TestShellProtocolChild.h @@ -0,0 +1,141 @@ +// +// Automatically generated by ipdlc. +// Edit at your own risk +// + +#ifndef mozilla_ipc_TestShellProtocolChild_h +#define mozilla_ipc_TestShellProtocolChild_h + +#include "mozilla/ipc/TestShellProtocol.h" +#include "mozilla/ipc/RPCChannel.h" + +namespace mozilla { +namespace ipc { + + +class /*NS_ABSTRACT_CLASS*/ TestShellProtocolChild : + public mozilla::ipc::RPCChannel::Listener +{ +protected: + typedef mozilla::ipc::String String; + typedef mozilla::ipc::StringArray StringArray; + + virtual nsresult AnswerSendCommand(const String& aCommand) = 0; + +private: + typedef IPC::Message Message; + typedef mozilla::ipc::RPCChannel Channel; + +public: + TestShellProtocolChild() : + mChannel(this) + { + } + + virtual ~TestShellProtocolChild() + { + } + + bool Open( + IPC::Channel* aChannel, + MessageLoop* aThread = 0) + { + return mChannel.Open(aChannel, aThread); + } + + void Close() + { + mChannel.Close(); + } + + virtual Result OnMessageReceived(const Message& msg) + { + switch (msg.type()) { + default: + { + return MsgNotKnown; + } + } + } + + virtual Result OnMessageReceived( + const Message& msg, + Message*& reply) + { + switch (msg.type()) { + default: + { + return MsgNotKnown; + } + } + } + + virtual Result OnCallReceived( + const Message& msg, + Message*& reply) + { + switch (msg.type()) { + case TestShellProtocol::Msg_SendCommand__ID: + { + String aCommand; + + if (!(TestShellProtocol::Msg_SendCommand::Read(&(msg), &(aCommand)))) { + return MsgPayloadError; + } + if (NS_FAILED(AnswerSendCommand(aCommand))) { + return MsgValueError; + } + + reply = new TestShellProtocol::Reply_SendCommand(); + reply->set_reply(); + return MsgProcessed; + } + default: + { + return MsgNotKnown; + } + } + } + +private: + Channel mChannel; + int mId; + int mPeerId; + mozilla::ipc::IProtocolManager* mManager; +}; + + +} // namespace ipc +} // namespace mozilla +#if 0 + +//----------------------------------------------------------------------------- +// Skeleton implementation of abstract actor class + +// Header file contents +class ActorImpl : + public TestShellProtocolChild +{ + virtual nsresult AnswerSendCommand(const String& aCommand); + ActorImpl(); + virtual ~ActorImpl(); +}; + + +// C++ file contents +nsresult ActorImpl::AnswerSendCommand(const String& aCommand) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +ActorImpl::ActorImpl() +{ +} + +ActorImpl::~ActorImpl() +{ +} + +#endif // if 0 + +#endif // ifndef mozilla_ipc_TestShellProtocolChild_h
new file mode 100644 --- /dev/null +++ b/ipc/testshell/TestShellProtocolParent.h @@ -0,0 +1,134 @@ +// +// Automatically generated by ipdlc. +// Edit at your own risk +// + +#ifndef mozilla_ipc_TestShellProtocolParent_h +#define mozilla_ipc_TestShellProtocolParent_h + +#include "mozilla/ipc/TestShellProtocol.h" +#include "mozilla/ipc/RPCChannel.h" + +namespace mozilla { +namespace ipc { + + +class /*NS_ABSTRACT_CLASS*/ TestShellProtocolParent : + public mozilla::ipc::RPCChannel::Listener +{ +protected: + typedef mozilla::ipc::String String; + typedef mozilla::ipc::StringArray StringArray; + + +private: + typedef IPC::Message Message; + typedef mozilla::ipc::RPCChannel Channel; + +public: + TestShellProtocolParent() : + mChannel(this) + { + } + + virtual ~TestShellProtocolParent() + { + } + + bool Open( + IPC::Channel* aChannel, + MessageLoop* aThread = 0) + { + return mChannel.Open(aChannel, aThread); + } + + void Close() + { + mChannel.Close(); + } + + nsresult CallSendCommand(const String& aCommand) + { + Message __reply; + Message* __msg; + __msg = new TestShellProtocol::Msg_SendCommand(aCommand); + __msg->set_routing_id(MSG_ROUTING_CONTROL); + if (!(mChannel.Call(__msg, &(__reply)))) { + return NS_ERROR_FAILURE; + } + if (!(TestShellProtocol::Reply_SendCommand::Read(&(__reply)))) { + return NS_ERROR_ILLEGAL_VALUE; + } + return NS_OK; + } + + virtual Result OnMessageReceived(const Message& msg) + { + switch (msg.type()) { + default: + { + return MsgNotKnown; + } + } + } + + virtual Result OnMessageReceived( + const Message& msg, + Message*& reply) + { + switch (msg.type()) { + default: + { + return MsgNotKnown; + } + } + } + + virtual Result OnCallReceived( + const Message& msg, + Message*& reply) + { + switch (msg.type()) { + default: + { + return MsgNotKnown; + } + } + } + +private: + Channel mChannel; + int mId; + int mPeerId; + mozilla::ipc::IProtocolManager* mManager; +}; + + +} // namespace ipc +} // namespace mozilla +#if 0 + +//----------------------------------------------------------------------------- +// Skeleton implementation of abstract actor class + +// Header file contents +class ActorImpl : + public TestShellProtocolParent +{ + ActorImpl(); + virtual ~ActorImpl(); +}; + + +// C++ file contents +ActorImpl::ActorImpl() +{ +} + +ActorImpl::~ActorImpl() +{ +} + +#endif // if 0 + +#endif // ifndef mozilla_ipc_TestShellProtocolParent_h
new file mode 100644 --- /dev/null +++ b/ipc/testshell/TestShellThread.cpp @@ -0,0 +1,77 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPCShell. + * + * The Initial Developer of the Original Code is + * Ben Turner <bent.mozilla@gmail.com>. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "TestShellThread.h" + +#include "XPCShellEnvironment.h" + +#include "base/message_loop.h" + +using mozilla::ipc::TestShellThread; +using mozilla::ipc::XPCShellEnvironment; + +TestShellThread::TestShellThread() +: mXPCShell(nsnull) +{ + +} + +TestShellThread::~TestShellThread() +{ + +} + +void +TestShellThread::Init() +{ + GeckoThread::Init(); + mXPCShell = XPCShellEnvironment::CreateEnvironment(); + if (mXPCShell) { + if (mTestShellChild.Open(channel(), owner_loop())) { + mTestShellChild.SetXPCShell(mXPCShell); + } + } +} + +void +TestShellThread::CleanUp() +{ + GeckoThread::CleanUp(); + if (mXPCShell) { + XPCShellEnvironment::DestroyEnvironment(mXPCShell); + mTestShellChild.Close(); + } +}
new file mode 100644 --- /dev/null +++ b/ipc/testshell/TestShellThread.h @@ -0,0 +1,66 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPCShell. + * + * The Initial Developer of the Original Code is + * Ben Turner <bent.mozilla@gmail.com>. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _IPC_TESTSHELL_TESTSHELLTHREAD_H_ +#define _IPC_TESTSHELL_TESTSHELLTHREAD_H_ + +#include "mozilla/ipc/GeckoThread.h" +#include "TestShellChild.h" + +namespace mozilla { +namespace ipc { + +class XPCShellEnvironment; + +class TestShellThread : public GeckoThread +{ +public: + TestShellThread(); + ~TestShellThread(); + +protected: + virtual void Init(); + virtual void CleanUp(); + +private: + XPCShellEnvironment* mXPCShell; + TestShellChild mTestShellChild; +}; + +} /* namespace ipc */ +} /* namespace mozilla */ + +#endif /* _IPC_TESTSHELL_TESTSHELLTHREAD_H_ */ \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/ipc/testshell/XPCShellEnvironment.cpp @@ -0,0 +1,1505 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPCShell. + * + * The Initial Developer of the Original Code is + * Ben Turner <bent.mozilla@gmail.com>. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "XPCShellEnvironment.h" + +#include <stdlib.h> +#include <errno.h> +#ifdef HAVE_IO_H +#include <io.h> /* for isatty() */ +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for isatty() */ +#endif + +#if defined(XP_MACOSX) +#include <Foundation/Foundation.h> +#endif + +#include "jsapi.h" +#include "jscntxt.h" +#include "jsdbgapi.h" +#include "jsprf.h" + +#include "mozilla/XPCOM.h" + +#include "nsIChannel.h" +#include "nsIClassInfo.h" +#include "nsIDirectoryService.h" +#include "nsIJSContextStack.h" +#include "nsIJSRuntimeService.h" +#include "nsIPrincipal.h" +#include "nsIScriptSecurityManager.h" +#include "nsIURI.h" +#include "nsIXPConnect.h" +#include "nsIXPCScriptable.h" + +#include "nsXULAppAPI.h" + +#include "TestShellParent.h" + +#define EXITCODE_RUNTIME_ERROR 3 +#define EXITCODE_FILE_NOT_FOUND 4 + +using mozilla::ipc::XPCShellEnvironment; +using mozilla::ipc::TestShellParent; + +namespace { + +static const char kDefaultRuntimeScriptFilename[] = "xpcshell.js"; + +class FullTrustSecMan : public nsIScriptSecurityManager +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIXPCSECURITYMANAGER + NS_DECL_NSISCRIPTSECURITYMANAGER + + FullTrustSecMan() { } + virtual ~FullTrustSecMan() { } + + void SetSystemPrincipal(nsIPrincipal *aPrincipal) { + mSystemPrincipal = aPrincipal; + } + +private: + nsCOMPtr<nsIPrincipal> mSystemPrincipal; +}; + +class XPCShellDirProvider : public nsIDirectoryServiceProvider +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDIRECTORYSERVICEPROVIDER + + XPCShellDirProvider() { } + ~XPCShellDirProvider() { } + + PRBool SetGREDir(const char *dir); + void ClearGREDir() { mGREDir = nsnull; } + +private: + nsCOMPtr<nsILocalFile> mGREDir; +}; + +inline XPCShellEnvironment* +Environment(JSContext* cx) +{ + XPCShellEnvironment* env = + static_cast<XPCShellEnvironment*>(JS_GetContextPrivate(cx)); + NS_ASSERTION(env, "Should never be null!"); + return env; +} + +static void +ScriptErrorReporter(JSContext *cx, + const char *message, + JSErrorReport *report) +{ + int i, j, k, n; + char *prefix = NULL, *tmp; + const char *ctmp; + JSStackFrame * fp = nsnull; + nsCOMPtr<nsIXPConnect> xpc; + + // Don't report an exception from inner JS frames as the callers may intend + // to handle it. + while ((fp = JS_FrameIterator(cx, &fp))) { + if (!JS_IsNativeFrame(cx, fp)) { + return; + } + } + + // In some cases cx->fp is null here so use XPConnect to tell us about inner + // frames. + if ((xpc = do_GetService(nsIXPConnect::GetCID()))) { + nsAXPCNativeCallContext *cc = nsnull; + xpc->GetCurrentNativeCallContext(&cc); + if (cc) { + nsAXPCNativeCallContext *prev = cc; + while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) { + PRUint16 lang; + if (NS_SUCCEEDED(prev->GetLanguage(&lang)) && + lang == nsAXPCNativeCallContext::LANG_JS) { + return; + } + } + } + } + + if (!report) { + fprintf(stderr, "%s\n", message); + return; + } + + /* Conditionally ignore reported warnings. */ + if (JSREPORT_IS_WARNING(report->flags) && + !Environment(cx)->ShouldReportWarnings()) { + return; + } + + if (report->filename) + prefix = JS_smprintf("%s:", report->filename); + if (report->lineno) { + tmp = prefix; + prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno); + JS_free(cx, tmp); + } + if (JSREPORT_IS_WARNING(report->flags)) { + tmp = prefix; + prefix = JS_smprintf("%s%swarning: ", + tmp ? tmp : "", + JSREPORT_IS_STRICT(report->flags) ? "strict " : ""); + JS_free(cx, tmp); + } + + /* embedded newlines -- argh! */ + while ((ctmp = strchr(message, '\n')) != 0) { + ctmp++; + if (prefix) fputs(prefix, stderr); + fwrite(message, 1, ctmp - message, stderr); + message = ctmp; + } + /* If there were no filename or lineno, the prefix might be empty */ + if (prefix) + fputs(prefix, stderr); + fputs(message, stderr); + + if (!report->linebuf) { + fputc('\n', stderr); + goto out; + } + + fprintf(stderr, ":\n%s%s\n%s", prefix, report->linebuf, prefix); + n = report->tokenptr - report->linebuf; + for (i = j = 0; i < n; i++) { + if (report->linebuf[i] == '\t') { + for (k = (j + 8) & ~7; j < k; j++) { + fputc('.', stderr); + } + continue; + } + fputc('.', stderr); + j++; + } + fputs("^\n", stderr); + out: + if (!JSREPORT_IS_WARNING(report->flags)) { + Environment(cx)->SetExitCode(EXITCODE_RUNTIME_ERROR); + } + JS_free(cx, prefix); +} + +JSContextCallback gOldContextCallback = NULL; + +static JSBool +ContextCallback(JSContext *cx, + uintN contextOp) +{ + if (gOldContextCallback && !gOldContextCallback(cx, contextOp)) + return JS_FALSE; + + if (contextOp == JSCONTEXT_NEW) { + JS_SetErrorReporter(cx, ScriptErrorReporter); + JS_SetVersion(cx, JSVERSION_LATEST); + } + return JS_TRUE; +} + +static JSBool +Print(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + uintN i, n; + JSString *str; + + for (i = n = 0; i < argc; i++) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + fprintf(stdout, "%s%s", i ? " " : "", JS_GetStringBytes(str)); + fflush(stdout); + } + n++; + if (n) + fputc('\n', stdout); + return JS_TRUE; +} + +static JSBool +GetLine(char *bufp, + FILE *file, + const char *prompt) +{ + char line[256]; + fprintf(stdout, prompt); + fflush(stdout); + if (!fgets(line, sizeof line, file)) + return JS_FALSE; + strcpy(bufp, line); + return JS_TRUE; +} + +static JSBool +ReadLine(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + // While 4096 might be quite arbitrary, this is something to be fixed in + // bug 105707. It is also the same limit as in ProcessFile. + char buf[4096]; + JSString *str; + + /* If a prompt was specified, construct the string */ + if (argc > 0) { + str = JS_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + argv[0] = STRING_TO_JSVAL(str); + } else { + str = JSVAL_TO_STRING(JS_GetEmptyStringValue(cx)); + } + + /* Get a line from the infile */ + if (!GetLine(buf, stdin, JS_GetStringBytes(str))) + return JS_FALSE; + + /* Strip newline character added by GetLine() */ + unsigned int buflen = strlen(buf); + if (buflen == 0) { + if (feof(stdin)) { + *rval = JSVAL_NULL; + return JS_TRUE; + } + } else if (buf[buflen - 1] == '\n') { + --buflen; + } + + /* Turn buf into a JSString */ + str = JS_NewStringCopyN(cx, buf, buflen); + if (!str) + return JS_FALSE; + + *rval = STRING_TO_JSVAL(str); + return JS_TRUE; +} + +static JSBool +Dump(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + JSString *str; + if (!argc) + return JS_TRUE; + + str = JS_ValueToString(cx, argv[0]); + if (!str) + return JS_FALSE; + + fputs(JS_GetStringBytes(str), stdout); + fflush(stdout); + return JS_TRUE; +} + +static JSBool +Load(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + uintN i; + JSString *str; + const char *filename; + JSScript *script; + JSBool ok; + jsval result; + FILE *file; + + for (i = 0; i < argc; i++) { + str = JS_ValueToString(cx, argv[i]); + if (!str) + return JS_FALSE; + argv[i] = STRING_TO_JSVAL(str); + filename = JS_GetStringBytes(str); + file = fopen(filename, "r"); + if (!file) { + JS_ReportError(cx, "cannot open file '%s' for reading", filename); + return JS_FALSE; + } + script = JS_CompileFileHandleForPrincipals(cx, obj, filename, file, + Environment(cx)->GetPrincipal()); + fclose(file); + if (!script) + return JS_FALSE; + + ok = !Environment(cx)->ShouldCompileOnly() + ? JS_ExecuteScript(cx, obj, script, &result) + : JS_TRUE; + JS_DestroyScript(cx, script); + if (!ok) + return JS_FALSE; + } + return JS_TRUE; +} + +static JSBool +Version(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + if (argc > 0 && JSVAL_IS_INT(argv[0])) + *rval = INT_TO_JSVAL(JS_SetVersion(cx, JSVersion(JSVAL_TO_INT(argv[0])))); + else + *rval = INT_TO_JSVAL(JS_GetVersion(cx)); + return JS_TRUE; +} + +static JSBool +BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + fprintf(stdout, "built on %s at %s\n", __DATE__, __TIME__); + return JS_TRUE; +} + +static JSBool +Quit(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + int exitCode = 0; + JS_ConvertArguments(cx, argc, argv, "/ i", &exitCode); + + XPCShellEnvironment* env = Environment(cx); + env->SetExitCode(exitCode); + env->SetIsQuitting(); + + return JS_FALSE; +} + +static JSBool +DumpXPC(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + int32 depth = 2; + + if (argc > 0) { + if (!JS_ValueToInt32(cx, argv[0], &depth)) + return JS_FALSE; + } + + nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID()); + if(xpc) + xpc->DebugDump((int16)depth); + return JS_TRUE; +} + +static JSBool +GC(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + JSRuntime *rt; + uint32 preBytes; + + rt = cx->runtime; + preBytes = rt->gcBytes; + JS_GC(cx); + fprintf(stdout, "before %lu, after %lu, break %08lx\n", + (unsigned long)preBytes, (unsigned long)rt->gcBytes, +#ifdef XP_UNIX + (unsigned long)sbrk(0) +#else + 0 +#endif + ); +#ifdef JS_GCMETER + js_DumpGCStats(rt, stdout); +#endif + return JS_TRUE; +} + +#ifdef DEBUG + +static JSBool +DumpHeap(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + char *fileName = NULL; + void* startThing = NULL; + uint32 startTraceKind = 0; + void *thingToFind = NULL; + size_t maxDepth = (size_t)-1; + void *thingToIgnore = NULL; + jsval *vp; + FILE *dumpFile; + JSBool ok; + + vp = &argv[0]; + if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) { + JSString *str; + + str = JS_ValueToString(cx, *vp); + if (!str) + return JS_FALSE; + *vp = STRING_TO_JSVAL(str); + fileName = JS_GetStringBytes(str); + } + + vp = &argv[1]; + if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) { + if (!JSVAL_IS_TRACEABLE(*vp)) + goto not_traceable_arg; + startThing = JSVAL_TO_TRACEABLE(*vp); + startTraceKind = JSVAL_TRACE_KIND(*vp); + } + + vp = &argv[2]; + if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) { + if (!JSVAL_IS_TRACEABLE(*vp)) + goto not_traceable_arg; + thingToFind = JSVAL_TO_TRACEABLE(*vp); + } + + vp = &argv[3]; + if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) { + uint32 depth; + + if (!JS_ValueToECMAUint32(cx, *vp, &depth)) + return JS_FALSE; + maxDepth = depth; + } + + vp = &argv[4]; + if (*vp != JSVAL_NULL && *vp != JSVAL_VOID) { + if (!JSVAL_IS_TRACEABLE(*vp)) + goto not_traceable_arg; + thingToIgnore = JSVAL_TO_TRACEABLE(*vp); + } + + if (!fileName) { + dumpFile = stdout; + } else { + dumpFile = fopen(fileName, "w"); + if (!dumpFile) { + fprintf(stderr, "dumpHeap: can't open %s: %s\n", + fileName, strerror(errno)); + return JS_FALSE; + } + } + + ok = JS_DumpHeap(cx, dumpFile, startThing, startTraceKind, thingToFind, + maxDepth, thingToIgnore); + if (dumpFile != stdout) + fclose(dumpFile); + return ok; + + not_traceable_arg: + fprintf(stderr, + "dumpHeap: argument %u is not null or a heap-allocated thing\n", + (unsigned)(vp - argv)); + return JS_FALSE; +} + +#endif /* DEBUG */ + +static JSBool +Clear(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + if (argc > 0 && !JSVAL_IS_PRIMITIVE(argv[0])) { + JS_ClearScope(cx, JSVAL_TO_OBJECT(argv[0])); + } else { + JS_ReportError(cx, "'clear' requires an object"); + return JS_FALSE; + } + return JS_TRUE; +} + +JSFunctionSpec gGlobalFunctions[] = +{ + {"print", Print, 0,0,0}, + {"readline", ReadLine, 1,0,0}, + {"load", Load, 1,0,0}, + {"quit", Quit, 0,0,0}, + {"version", Version, 1,0,0}, + {"build", BuildDate, 0,0,0}, + {"dumpXPC", DumpXPC, 1,0,0}, + {"dump", Dump, 1,0,0}, + {"gc", GC, 0,0,0}, + {"clear", Clear, 1,0,0}, +#ifdef DEBUG + {"dumpHeap", DumpHeap, 5,0,0}, +#endif +#ifdef MOZ_SHARK + {"startShark", js_StartShark, 0,0,0}, + {"stopShark", js_StopShark, 0,0,0}, + {"connectShark", js_ConnectShark, 0,0,0}, + {"disconnectShark", js_DisconnectShark, 0,0,0}, +#endif +#ifdef MOZ_CALLGRIND + {"startCallgrind", js_StartCallgrind, 0,0,0}, + {"stopCallgrind", js_StopCallgrind, 0,0,0}, + {"dumpCallgrind", js_DumpCallgrind, 1,0,0}, +#endif + {nsnull,nsnull,0,0,0} +}; + +JSClass gGlobalClass = +{ + "global", 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub +}; + +static JSBool +EnvironmentSetProperty(JSContext *cx, + JSObject *obj, + jsval id, + jsval *vp) +{ +/* XXX porting may be easy, but these don't seem to supply setenv by default */ +#if !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS + JSString *idstr, *valstr; + const char *name, *value; + int rv; + + idstr = JS_ValueToString(cx, id); + valstr = JS_ValueToString(cx, *vp); + if (!idstr || !valstr) + return JS_FALSE; + name = JS_GetStringBytes(idstr); + value = JS_GetStringBytes(valstr); +#if defined XP_WIN || defined HPUX || defined OSF1 || defined IRIX \ + || defined SCO + { + char *waste = JS_smprintf("%s=%s", name, value); + if (!waste) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + rv = putenv(waste); +#ifdef XP_WIN + /* + * HPUX9 at least still has the bad old non-copying putenv. + * + * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv + * that will crash if you pass it an auto char array (so it must place + * its argument directly in the char *environ[] array). + */ + free(waste); +#endif + } +#else + rv = setenv(name, value, 1); +#endif + if (rv < 0) { + JS_ReportError(cx, "can't set envariable %s to %s", name, value); + return JS_FALSE; + } + *vp = STRING_TO_JSVAL(valstr); +#endif /* !defined XP_BEOS && !defined XP_OS2 && !defined SOLARIS */ + return JS_TRUE; +} + +static JSBool +EnvironmentEnumerate(JSContext *cx, + JSObject *obj) +{ + static JSBool reflected; + char **evp, *name, *value; + JSString *valstr; + JSBool ok; + + if (reflected) + return JS_TRUE; + + for (evp = (char **)JS_GetPrivate(cx, obj); (name = *evp) != NULL; evp++) { + value = strchr(name, '='); + if (!value) + continue; + *value++ = '\0'; + valstr = JS_NewStringCopyZ(cx, value); + if (!valstr) { + ok = JS_FALSE; + } else { + ok = JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), + NULL, NULL, JSPROP_ENUMERATE); + } + value[-1] = '='; + if (!ok) + return JS_FALSE; + } + + reflected = JS_TRUE; + return JS_TRUE; +} + +static JSBool +EnvironmentResolve(JSContext *cx, + JSObject *obj, + jsval id, + uintN flags, + JSObject **objp) +{ + JSString *idstr, *valstr; + const char *name, *value; + + if (flags & JSRESOLVE_ASSIGNING) + return JS_TRUE; + + idstr = JS_ValueToString(cx, id); + if (!idstr) + return JS_FALSE; + name = JS_GetStringBytes(idstr); + value = getenv(name); + if (value) { + valstr = JS_NewStringCopyZ(cx, value); + if (!valstr) + return JS_FALSE; + if (!JS_DefineProperty(cx, obj, name, STRING_TO_JSVAL(valstr), + NULL, NULL, JSPROP_ENUMERATE)) { + return JS_FALSE; + } + *objp = obj; + } + return JS_TRUE; +} + +JSClass gEnvironmentClass = +{ + "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, EnvironmentSetProperty, + EnvironmentEnumerate, (JSResolveOp) EnvironmentResolve, JS_ConvertStub, + JS_FinalizeStub +}; + +typedef enum JSShellErrNum +{ +#define MSG_DEF(name, number, count, exception, format) \ + name = number, +#include "jsshell.msg" +#undef MSG_DEF + JSShellErr_Limit +#undef MSGDEF +} JSShellErrNum; + +JSErrorFormatString gErrorFormatString[JSErr_Limit] = +{ +#define MSG_DEF(name, number, count, exception, format) \ + { format, count } , +#include "jsshell.msg" +#undef MSG_DEF +}; + +static const JSErrorFormatString * +GetErrorMessage(void *userRef, + const char *locale, + const uintN errorNumber) +{ + if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit)) + return &gErrorFormatString[errorNumber]; + + return NULL; +} + +static int +PrintUsage(void) +{ + fprintf(stderr, "%s\n", JS_GetImplementationVersion()); + fprintf(stderr, "usage: xpcshell [-g gredir] [-PswWxCij] [-v version] [-f scriptfile] [-e script] [scriptfile] [scriptarg...]\n"); + return 2; +} + +static void +ProcessFile(JSContext *cx, + JSObject *obj, + const char *filename, + FILE *file, + JSBool forceTTY) +{ + XPCShellEnvironment* env = Environment(cx); + + JSScript *script; + jsval result; + int lineno, startline; + JSBool ok, hitEOF; + char *bufp, buffer[4096]; + JSString *str; + + if (forceTTY) { + file = stdin; + } + else +#ifdef HAVE_ISATTY + if (!isatty(fileno(file))) +#endif + { + /* + * It's not interactive - just execute it. + * + * Support the UNIX #! shell hack; gobble the first line if it starts + * with '#'. TODO - this isn't quite compatible with sharp variables, + * as a legal js program (using sharp variables) might start with '#'. + * But that would require multi-character lookahead. + */ + int ch = fgetc(file); + if (ch == '#') { + while((ch = fgetc(file)) != EOF) { + if(ch == '\n' || ch == '\r') + break; + } + } + ungetc(ch, file); + + JSAutoRequest ar(cx); + + JSScript* script = + JS_CompileFileHandleForPrincipals(cx, obj, filename, file, + env->GetPrincipal()); + if (script) { + if (!env->ShouldCompileOnly()) + (void)JS_ExecuteScript(cx, obj, script, &result); + JS_DestroyScript(cx, script); + } + + return; + } + + /* It's an interactive filehandle; drop into read-eval-print loop. */ + lineno = 1; + hitEOF = JS_FALSE; + do { + bufp = buffer; + *bufp = '\0'; + + JSAutoRequest ar(cx); + + /* + * Accumulate lines until we get a 'compilable unit' - one that either + * generates an error (before running out of source) or that compiles + * cleanly. This should be whenever we get a complete statement that + * coincides with the end of a line. + */ + startline = lineno; + do { + if (!GetLine(bufp, file, startline == lineno ? "js> " : "")) { + hitEOF = JS_TRUE; + break; + } + bufp += strlen(bufp); + lineno++; + } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer))); + + /* Clear any pending exception from previous failed compiles. */ + JS_ClearPendingException(cx); + script = + JS_CompileScriptForPrincipals(cx, obj, env->GetPrincipal(), buffer, + strlen(buffer), "typein", startline); + if (script) { + JSErrorReporter older; + + if (!env->ShouldCompileOnly()) { + ok = JS_ExecuteScript(cx, obj, script, &result); + if (ok && result != JSVAL_VOID) { + /* Suppress error reports from JS_ValueToString(). */ + older = JS_SetErrorReporter(cx, NULL); + str = JS_ValueToString(cx, result); + JS_SetErrorReporter(cx, older); + + if (str) + fprintf(stdout, "%s\n", JS_GetStringBytes(str)); + else + ok = JS_FALSE; + } + } + JS_DestroyScript(cx, script); + } + } while (!hitEOF && !env->IsQuitting()); + + fprintf(stdout, "\n"); +} + +} /* anonymous namespace */ + +NS_INTERFACE_MAP_BEGIN(FullTrustSecMan) + NS_INTERFACE_MAP_ENTRY(nsIXPCSecurityManager) + NS_INTERFACE_MAP_ENTRY(nsIScriptSecurityManager) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCSecurityManager) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(FullTrustSecMan) +NS_IMPL_RELEASE(FullTrustSecMan) + +NS_IMETHODIMP +FullTrustSecMan::CanCreateWrapper(JSContext * aJSContext, + const nsIID & aIID, + nsISupports *aObj, + nsIClassInfo *aClassInfo, + void * *aPolicy) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CanCreateInstance(JSContext * aJSContext, + const nsCID & aCID) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CanGetService(JSContext * aJSContext, + const nsCID & aCID) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CanAccess(PRUint32 aAction, + nsAXPCNativeCallContext *aCallContext, + JSContext * aJSContext, + JSObject * aJSObject, + nsISupports *aObj, + nsIClassInfo *aClassInfo, + jsval aName, + void * *aPolicy) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CheckPropertyAccess(JSContext * aJSContext, + JSObject * aJSObject, + const char *aClassName, + jsval aProperty, + PRUint32 aAction) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CheckConnect(JSContext * aJSContext, + nsIURI *aTargetURI, + const char *aClassName, + const char *aProperty) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CheckLoadURIFromScript(JSContext * cx, + nsIURI *uri) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CheckLoadURIWithPrincipal(nsIPrincipal *aPrincipal, + nsIURI *uri, + PRUint32 flags) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CheckLoadURI(nsIURI *from, + nsIURI *uri, + PRUint32 flags) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CheckLoadURIStrWithPrincipal(nsIPrincipal *aPrincipal, + const nsACString & uri, + PRUint32 flags) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CheckLoadURIStr(const nsACString & from, + const nsACString & uri, + PRUint32 flags) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CheckFunctionAccess(JSContext * cx, + void * funObj, + void * targetObj) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CanExecuteScripts(JSContext * cx, + nsIPrincipal *principal, + PRBool *_retval) +{ + *_retval = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::GetSubjectPrincipal(nsIPrincipal **_retval) +{ + NS_IF_ADDREF(*_retval = mSystemPrincipal); + return *_retval ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +FullTrustSecMan::GetSystemPrincipal(nsIPrincipal **_retval) +{ + NS_IF_ADDREF(*_retval = mSystemPrincipal); + return *_retval ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +FullTrustSecMan::GetCertificatePrincipal(const nsACString & aCertFingerprint, + const nsACString & aSubjectName, + const nsACString & aPrettyName, + nsISupports *aCert, + nsIURI *aURI, + nsIPrincipal **_retval) +{ + NS_IF_ADDREF(*_retval = mSystemPrincipal); + return *_retval ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +FullTrustSecMan::GetCodebasePrincipal(nsIURI *aURI, + nsIPrincipal **_retval) +{ + NS_IF_ADDREF(*_retval = mSystemPrincipal); + return *_retval ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +FullTrustSecMan::RequestCapability(nsIPrincipal *principal, + const char *capability, + PRInt16 *_retval) +{ + *_retval = nsIPrincipal::ENABLE_GRANTED; + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::IsCapabilityEnabled(const char *capability, + PRBool *_retval) +{ + *_retval = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::EnableCapability(const char *capability) +{ + return NS_OK;; +} + +NS_IMETHODIMP +FullTrustSecMan::RevertCapability(const char *capability) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::DisableCapability(const char *capability) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::SetCanEnableCapability(const nsACString & certificateFingerprint, + const char *capability, + PRInt16 canEnable) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::GetObjectPrincipal(JSContext * cx, + JSObject * obj, + nsIPrincipal **_retval) +{ + NS_IF_ADDREF(*_retval = mSystemPrincipal); + return *_retval ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +FullTrustSecMan::SubjectPrincipalIsSystem(PRBool *_retval) +{ + *_retval = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CheckSameOrigin(JSContext * aJSContext, + nsIURI *aTargetURI) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::CheckSameOriginURI(nsIURI *aSourceURI, + nsIURI *aTargetURI, + PRBool reportError) +{ + return NS_OK; +} + +NS_IMETHODIMP +FullTrustSecMan::GetPrincipalFromContext(JSContext * cx, + nsIPrincipal **_retval) +{ + NS_IF_ADDREF(*_retval = mSystemPrincipal); + return *_retval ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +FullTrustSecMan::GetChannelPrincipal(nsIChannel *aChannel, + nsIPrincipal **_retval) +{ + NS_IF_ADDREF(*_retval = mSystemPrincipal); + return *_retval ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +FullTrustSecMan::IsSystemPrincipal(nsIPrincipal *aPrincipal, + PRBool *_retval) +{ + *_retval = aPrincipal == mSystemPrincipal; + return NS_OK; +} + +NS_IMETHODIMP_(nsIPrincipal *) +FullTrustSecMan::GetCxSubjectPrincipal(JSContext *cx) +{ + return mSystemPrincipal; +} + +NS_IMETHODIMP_(nsIPrincipal *) +FullTrustSecMan::GetCxSubjectPrincipalAndFrame(JSContext *cx, + JSStackFrame **fp) +{ + *fp = nsnull; + return mSystemPrincipal; +} + +NS_IMETHODIMP_(nsrefcnt) +XPCShellDirProvider::AddRef() +{ + return 2; +} + +NS_IMETHODIMP_(nsrefcnt) +XPCShellDirProvider::Release() +{ + return 1; +} + +NS_IMPL_QUERY_INTERFACE1(XPCShellDirProvider, nsIDirectoryServiceProvider) + +PRBool +XPCShellDirProvider::SetGREDir(const char *dir) +{ + nsresult rv = XRE_GetFileFromPath(dir, getter_AddRefs(mGREDir)); + return NS_SUCCEEDED(rv); +} + +NS_IMETHODIMP +XPCShellDirProvider::GetFile(const char *prop, + PRBool *persistent, + nsIFile* *result) +{ + if (mGREDir && !strcmp(prop, NS_GRE_DIR)) { + *persistent = PR_TRUE; + NS_ADDREF(*result = mGREDir); + return NS_OK; + } + + return NS_ERROR_FAILURE; +} + +// static +XPCShellEnvironment* +XPCShellEnvironment::CreateEnvironment() +{ + XPCShellEnvironment* env = new XPCShellEnvironment(); + if (env && !env->Init()) { + delete env; + env = nsnull; + } + return env; +} + +// static +void +XPCShellEnvironment::DestroyEnvironment(XPCShellEnvironment* aEnv) +{ + delete aEnv; +} + +XPCShellEnvironment::XPCShellEnvironment() +: mCx(NULL), + mJSPrincipals(NULL), + mExitCode(0), + mQuitting(JS_FALSE), + mReportWarnings(JS_TRUE), + mCompileOnly(JS_FALSE), + mParent(nsnull) +{ +} + +XPCShellEnvironment::~XPCShellEnvironment() +{ + if (mCx) { + JS_BeginRequest(mCx); + + JSObject* global = GetGlobalObject(); + if (global) { + JS_ClearScope(mCx, global); + } + mGlobalHolder.Release(); + + JS_GC(mCx); + + if (mCxStack) { + JSContext *oldCx; + mCxStack->Pop(&oldCx); + NS_ASSERTION(oldCx == mCx, "JS thread context push/pop mismatch"); + + JS_GC(mCx); + } + + if (mJSPrincipals) { + JSPRINCIPALS_DROP(mCx, mJSPrincipals); + } + + JSRuntime* rt = gOldContextCallback ? JS_GetRuntime(mCx) : NULL; + + JS_DestroyContext(mCx); + + if (gOldContextCallback) { + NS_ASSERTION(rt, "Should never be null!"); + JS_SetContextCallback(rt, gOldContextCallback); + gOldContextCallback = NULL; + } + } +} + +bool +XPCShellEnvironment::Init() +{ + nsresult rv; + +#ifdef HAVE_SETBUF + // unbuffer stdout so that output is in the correct order; note that stderr + // is unbuffered by default + setbuf(stdout, 0); +#endif + + nsCOMPtr<nsIJSRuntimeService> rtsvc = + do_GetService("@mozilla.org/js/xpc/RuntimeService;1"); + if (!rtsvc) { + NS_ERROR("failed to get nsJSRuntimeService!"); + return false; + } + + JSRuntime *rt; + if (NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) { + NS_ERROR("failed to get JSRuntime from nsJSRuntimeService!"); + return false; + } + + if (!mGlobalHolder.Hold(rt)) { + NS_ERROR("Can't protect global object!"); + return false; + } + + gOldContextCallback = JS_SetContextCallback(rt, ContextCallback); + + JSContext *cx = JS_NewContext(rt, 8192); + if (!cx) { + NS_ERROR("JS_NewContext failed!"); + + JS_SetContextCallback(rt, gOldContextCallback); + gOldContextCallback = NULL; + + return false; + } + mCx = cx; + + JS_SetContextPrivate(cx, this); + + nsCOMPtr<nsIXPConnect> xpc = + do_GetService(nsIXPConnect::GetCID()); + if (!xpc) { + NS_ERROR("failed to get nsXPConnect service!"); + return false; + } + + nsRefPtr<FullTrustSecMan> secman(new FullTrustSecMan()); + xpc->SetSecurityManagerForJSContext(cx, secman, 0xFFFF); + + nsCOMPtr<nsIPrincipal> principal; + + nsCOMPtr<nsIScriptSecurityManager> securityManager = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv) && securityManager) { + rv = securityManager->GetSystemPrincipal(getter_AddRefs(principal)); + if (NS_FAILED(rv)) { + fprintf(stderr, "+++ Failed to obtain SystemPrincipal from ScriptSecurityManager service.\n"); + } else { + // fetch the JS principals and stick in a global + rv = principal->GetJSPrincipals(cx, &mJSPrincipals); + if (NS_FAILED(rv)) { + fprintf(stderr, "+++ Failed to obtain JS principals from SystemPrincipal.\n"); + } + secman->SetSystemPrincipal(principal); + } + } else { + fprintf(stderr, "+++ Failed to get ScriptSecurityManager service, running without principals"); + } + + nsCOMPtr<nsIJSContextStack> cxStack = + do_GetService("@mozilla.org/js/xpc/ContextStack;1"); + if (!cxStack) { + NS_ERROR("failed to get the nsThreadJSContextStack service!"); + return false; + } + + if(NS_FAILED(cxStack->Push(cx))) { + NS_ERROR("failed to push the current JSContext on the nsThreadJSContextStack!"); + return false; + } + mCxStack = cxStack; + + nsCOMPtr<nsIXPCScriptable> backstagePass; + rv = rtsvc->GetBackstagePass(getter_AddRefs(backstagePass)); + if (NS_FAILED(rv)) { + NS_ERROR("Failed to get backstage pass from rtsvc!"); + return false; + } + + nsCOMPtr<nsIXPConnectJSObjectHolder> holder; + rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass, + NS_GET_IID(nsISupports), + nsIXPConnect:: + FLAG_SYSTEM_GLOBAL_OBJECT, + getter_AddRefs(holder)); + if (NS_FAILED(rv)) { + NS_ERROR("InitClassesWithNewWrappedGlobal failed!"); + return false; + } + + JSObject *globalObj; + rv = holder->GetJSObject(&globalObj); + if (NS_FAILED(rv)) { + NS_ERROR("Failed to get global JSObject!"); + return false; + } + + + { + JSAutoRequest ar(cx); + + if (!JS_DefineFunctions(cx, globalObj, gGlobalFunctions)) { + NS_ERROR("JS_DefineFunctions failed!"); + return false; + } + +#if 0 // Just until we care enough to get then environment strings. + JSObject *envObj = JS_DefineObject(cx, globalObj, "environment", + &gEnvironmentClass, NULL, 0); + if (!envObj || !JS_SetPrivate(cx, envObj, envp)) { + NS_ERROR("Failed to make environment object!"); + return false; + } +#endif + + } + + mGlobalHolder = globalObj; + + FILE* runtimeScriptFile = fopen(kDefaultRuntimeScriptFilename, "r"); + if (runtimeScriptFile) { + fprintf(stdout, "[loading '%s'...]\n", kDefaultRuntimeScriptFilename); + ProcessFile(cx, globalObj, kDefaultRuntimeScriptFilename, + runtimeScriptFile, JS_FALSE); + fclose(runtimeScriptFile); + } + + return true; +} + +void +XPCShellEnvironment::Process(const char* aFilename, + JSBool aIsInteractive) +{ + NS_ASSERTION(GetGlobalObject(), "Should never be null!"); + + FILE* file; + if (!aFilename || aIsInteractive) { + file = stdin; + } else { + file = fopen(aFilename, "r"); + if (!file) { + JS_ReportErrorNumber(mCx, GetErrorMessage, NULL, + JSSMSG_CANT_OPEN, + aFilename, strerror(errno)); + mExitCode = EXITCODE_FILE_NOT_FOUND; + return; + } + } + + ProcessFile(mCx, GetGlobalObject(), aFilename, file, aIsInteractive); + if (file != stdin) + fclose(file); +} + +namespace { + +static JSBool +SendCommand(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + if (argc != 1) { + JS_ReportError(cx, "Function takes only one argument!"); + return JS_FALSE; + } + + JSString* str = JS_ValueToString(cx, argv[0]); + if (!str) { + JS_ReportError(cx, "Could not convert argument to string!"); + return JS_FALSE; + } + + return Environment(cx)->DoSendCommand(str); +} + +} /* anonymous namespace */ + +bool +XPCShellEnvironment::DefineSendCommand(TestShellParent* aParent) +{ + mParent = aParent; + + JSAutoRequest ar(mCx); + + JSFunction* fun = JS_DefineFunction(mCx, GetGlobalObject(), "sendCommand", + SendCommand, 1, JSPROP_ENUMERATE); + if (!fun) { + NS_WARNING("Failed to define sendCommand function!"); + return false; + } + + return true; +} + +JSBool +XPCShellEnvironment::DoSendCommand(JSString* aCommand) +{ + String str(JS_GetStringBytes(aCommand)); + + nsresult rv = mParent->CallSendCommand(str); + return NS_SUCCEEDED(rv) ? JS_TRUE : JS_FALSE; +} + +bool +XPCShellEnvironment::EvaluateString(const std::string& aString) +{ + JSAutoRequest ar(mCx); + + JS_ClearPendingException(mCx); + + JSObject* global = GetGlobalObject(); + + JSScript* script = + JS_CompileScriptForPrincipals(mCx, global, GetPrincipal(), + aString.c_str(), aString.length(), + "typein", 0); + if (!script) { + return false; + } + + if (!ShouldCompileOnly()) { + jsval result; + JSBool ok = JS_ExecuteScript(mCx, global, script, &result); + if (ok && result != JSVAL_VOID) { + JSErrorReporter old = JS_SetErrorReporter(mCx, NULL); + JSString* str = JS_ValueToString(mCx, result); + JS_SetErrorReporter(mCx, old); + + if (str) { + fprintf(stdout, "%s\n", JS_GetStringBytes(str)); + } + } + } + + JS_DestroyScript(mCx, script); + + return true; +}
new file mode 100644 --- /dev/null +++ b/ipc/testshell/XPCShellEnvironment.h @@ -0,0 +1,133 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPCShell. + * + * The Initial Developer of the Original Code is + * Ben Turner <bent.mozilla@gmail.com>. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _IPC_TESTSHELL_XPCSHELLENVIRONMENT_H_ +#define _IPC_TESTSHELL_XPCSHELLENVIRONMENT_H_ + +#include "base/basictypes.h" + +#include <string> +#include <stdio.h> +#include "nsCOMPtr.h" +#include "nsDebug.h" +#include "nsAutoJSValHolder.h" + +struct JSContext; +struct JSObject; +struct JSPrincipals; + +class nsIJSContextStack; + +namespace mozilla { +namespace ipc { + +class TestShellParent; + +class XPCShellEnvironment +{ +public: + static XPCShellEnvironment* CreateEnvironment(); + static void DestroyEnvironment(XPCShellEnvironment* aEnv); + + void Process(const char* aFilename = nsnull, + JSBool aIsInteractive = JS_FALSE); + + bool DefineSendCommand(TestShellParent* aParent); + + JSBool DoSendCommand(JSString* aCommand); + + bool EvaluateString(const std::string& aString); + + JSPrincipals* GetPrincipal() { + return mJSPrincipals; + } + + JSObject* GetGlobalObject() { + return mGlobalHolder.ToJSObject(); + } + + void SetExitCode(int aExitCode) { + mExitCode = aExitCode; + } + int ExitCode() { + return mExitCode; + } + + void SetIsQuitting() { + mQuitting = JS_TRUE; + } + JSBool IsQuitting() { + return mQuitting; + } + + void SetShouldReportWarnings(JSBool aReportWarnings) { + mReportWarnings = aReportWarnings; + } + JSBool ShouldReportWarnings() { + return mReportWarnings; + } + + void SetShouldCompoleOnly(JSBool aCompileOnly) { + mCompileOnly = aCompileOnly; + } + JSBool ShouldCompileOnly() { + return mCompileOnly; + } + +protected: + XPCShellEnvironment(); + ~XPCShellEnvironment(); + + bool Init(); + +private: + JSContext* mCx; + nsAutoJSValHolder mGlobalHolder; + nsCOMPtr<nsIJSContextStack> mCxStack; + JSPrincipals* mJSPrincipals; + + int mExitCode; + JSBool mQuitting; + JSBool mReportWarnings; + JSBool mCompileOnly; + + TestShellParent* mParent; +}; + +} /* namespace ipc */ +} /* namespace mozilla */ + +#endif /* _IPC_TESTSHELL_XPCSHELLENVIRONMENT_H_ */ \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/ipc/testshell/app/Makefile.in @@ -0,0 +1,75 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla IPCShell. +# +# The Initial Developer of the Original Code is +# Ben Turner <bent.mozilla@gmail.com>. +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = ipcshell +PROGRAM = $(MODULE)$(BIN_SUFFIX) +ENABLE_CXX_EXCEPTIONS = 1 + +LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre + +CPPSRCS = \ + TestShellApp.cpp \ + $(NULL) + +LIBS = \ + $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \ + $(LIBXUL_LIBS) \ + $(MOZ_JS_LIBS) \ + $(NSPR_LIBS) \ + $(DEPTH)/ipc/testshell/ipcshell_s.$(LIB_SUFFIX) \ + $(NULL) + +NSDISTMODE = copy + +include $(topsrcdir)/config/config.mk + +ifdef _MSC_VER +ifdef WINCE +WIN32_EXE_LDFLAGS += -ENTRY:mainWCRTStartup +else +WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup +endif +endif + +include $(topsrcdir)/ipc/chromium/chromium-config.mk +include $(topsrcdir)/config/rules.mk
new file mode 100644 --- /dev/null +++ b/ipc/testshell/app/TestShellApp.cpp @@ -0,0 +1,48 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPCShell. + * + * The Initial Developer of the Original Code is + * Ben Turner <bent.mozilla@gmail.com>. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsXULAppAPI.h" + +#if defined(XP_WIN) +#include <windows.h> +#include "nsWindowsWMain.cpp" +#endif + +int +main(int argc, char* argv[]) +{ + return XRE_RunTestShell(argc, argv); +}
--- a/toolkit/library/dlldeps-xul.cpp +++ b/toolkit/library/dlldeps-xul.cpp @@ -44,9 +44,14 @@ void xxxNeverCalledXUL() XRE_GetStaticComponents(nsnull, nsnull); XRE_LockProfileDirectory(nsnull, nsnull); XRE_InitEmbedding(nsnull, nsnull, nsnull, nsnull, 0); XRE_NotifyProfile(); XRE_TermEmbedding(); XRE_CreateAppData(nsnull, nsnull); XRE_ParseAppData(nsnull, nsnull); XRE_FreeAppData(nsnull); + XRE_ChildProcessTypeToString(GeckoChildProcess_Default); + XRE_StringToChildProcessType(""); + XRE_InitChildProcess(0, nsnull); + XRE_InitParentProcess(0, nsnull, nsnull, nsnull); + XRE_RunTestShell(0, nsnull); }
--- a/toolkit/library/libxul-config.mk +++ b/toolkit/library/libxul-config.mk @@ -92,16 +92,17 @@ endif # dependent libraries ifdef MOZ_IPC STATIC_LIBS += \ domipc_s \ domplugins_s \ mozipc_s \ chromium_s \ + ipcshell_s \ $(NULL) ifeq (Linux,$(OS_ARCH)) OS_LIBS += -lrt endif ifeq (WINNT,$(OS_ARCH)) OS_LIBS += psapi.lib endif
--- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -51,37 +51,49 @@ #include "nsIAppStartupNotifier.h" #include "nsIDirectoryService.h" #include "nsILocalFile.h" #include "nsIToolkitChromeRegistry.h" #include "nsIToolkitProfile.h" #include "nsAppDirectoryServiceDefs.h" #include "nsAppRunner.h" +#include "nsAutoRef.h" #include "nsDirectoryServiceDefs.h" #include "nsStaticComponents.h" #include "nsString.h" #include "nsThreadUtils.h" #include "nsWidgetsCID.h" #include "nsXREDirProvider.h" #include "mozilla/ipc/GeckoChildProcessHost.h" #include "mozilla/ipc/GeckoThread.h" #include "ScopedXREEmbed.h" #include "mozilla/plugins/PluginThreadChild.h" #include "TabThread.h" +#include "mozilla/ipc/TestShellParent.h" +#include "mozilla/ipc/TestShellThread.h" +#include "mozilla/ipc/XPCShellEnvironment.h" +#include "mozilla/Monitor.h" + using mozilla::ipc::BrowserProcessSubThread; using mozilla::ipc::GeckoChildProcessHost; using mozilla::ipc::GeckoThread; using mozilla::ipc::ScopedXREEmbed; using mozilla::plugins::PluginThreadChild; using mozilla::tabs::TabThread; +using mozilla::ipc::TestShellParent; +using mozilla::ipc::TestShellThread; +using mozilla::ipc::XPCShellEnvironment; + +using mozilla::Monitor; +using mozilla::MonitorAutoEnter; static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); void XRE_GetStaticComponents(nsStaticModuleInfo const **aStaticComponents, PRUint32 *aComponentCount) { *aStaticComponents = kPStaticModules; @@ -198,25 +210,21 @@ XRE_ChildProcessTypeToString(GeckoChildP } GeckoChildProcessType XRE_StringToChildProcessType(const char* aProcessTypeString) { for (int i = 0; i < (int) NS_ARRAY_LENGTH(kGeckoChildProcessTypeString); ++i) { - const char* procString = kGeckoChildProcessTypeString[i]; - if (!procString) { - return GeckoChildProcess_Invalid; - } - if (!strcmp(procString, aProcessTypeString)) { + if (!strcmp(kGeckoChildProcessTypeString[i], aProcessTypeString)) { return static_cast<GeckoChildProcessType>(i); } } - NS_NOTREACHED("error"); + return GeckoChildProcess_Invalid; } nsresult XRE_InitChildProcess(int aArgc, char* aArgv[], GeckoChildProcessType aProcess) { NS_ENSURE_ARG_MIN(aArgc, 1); @@ -232,31 +240,35 @@ XRE_InitChildProcess(int aArgc, #endif } base::AtExitManager exitManager; CommandLine::Init(aArgc, aArgv); MessageLoopForIO mainMessageLoop; { - GeckoThread* mainThread; + ChildThread* mainThread; switch (aProcess) { case GeckoChildProcess_Default: mainThread = new GeckoThread(); break; case GeckoChildProcess_Plugin: mainThread = new PluginThreadChild(); break; case GeckoChildProcess_Tab: mainThread = new TabThread(); break; + case GeckoChildProcess_TestShell: + mainThread = new TestShellThread(); + break; + default: NS_RUNTIMEABORT("Unknown main thread class"); } ChildProcess process(mainThread); // Do IPC event loop MessageLoop::current()->Run(); @@ -354,30 +366,146 @@ XRE_InitParentProcess(int aArgc, return NS_OK; } namespace { class CreateChildProcess : public Task { public: + CreateChildProcess(Monitor* aMonitor, + IPC::Channel** aChannelPtr) + : mMonitor(aMonitor), + mChannelPtr(aChannelPtr) + { + NS_ASSERTION(aMonitor, "Null ptr!"); + } + virtual void Run() { - GeckoChildProcessHost* host = new GeckoChildProcessHost(); - if (!host->Launch()) { - delete host; + GeckoChildProcessHost* host = + new GeckoChildProcessHost(GeckoChildProcess_TestShell); + if (host) { + if (!host->Launch()) { + delete host; + } + // ChildProcessHost deletes itself once the child process exits, on + // windows at least... + *mChannelPtr = host->GetChannel(); } - // ChildProcessHost deletes itself once the child process exits, on windows - // at least... + + MonitorAutoEnter ae(*mMonitor); + mMonitor->Notify(); + } + +private: + Monitor* mMonitor; + IPC::Channel** mChannelPtr; +}; + +class QuitRunnable : public nsRunnable +{ +public: + NS_IMETHOD Run() { + nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID)); + NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); + + return appShell->Exit(); + } +}; + +NS_SPECIALIZE_TEMPLATE +class nsAutoRefTraits<XPCShellEnvironment> : + public nsPointerRefTraits<XPCShellEnvironment> +{ +public: + void Release(XPCShellEnvironment* aEnv) { + XPCShellEnvironment::DestroyEnvironment(aEnv); } }; +IPC::Channel* +LaunchTestShellChildProcess() +{ + Monitor mon("LaunchChildProcess monitor"); + + IPC::Channel* channel = nsnull; + + CreateChildProcess* creator = new CreateChildProcess(&mon, &channel); + NS_ENSURE_TRUE(creator, nsnull); + + MessageLoop* ioLoop = + BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO); + NS_ENSURE_TRUE(ioLoop, nsnull); + + { + MonitorAutoEnter ae(mon); + + ioLoop->PostTask(FROM_HERE, creator); + mon.Wait(); + } + + return channel; +} + +int +TestShellMain(int argc, char** argv) +{ + // Make sure we quit when we exit this function. + nsIRunnable* quitRunnable = new QuitRunnable(); + NS_ENSURE_TRUE(quitRunnable, 1); + + nsresult rv = NS_DispatchToCurrentThread(quitRunnable); + NS_ENSURE_SUCCESS(rv, 1); + + nsAutoRef<XPCShellEnvironment> env(XPCShellEnvironment::CreateEnvironment()); + NS_ENSURE_TRUE(env, 1); + + IPC::Channel* channel = LaunchTestShellChildProcess(); + NS_ENSURE_TRUE(channel, 1); + + TestShellParent testShellParent; + if (!testShellParent.Open(channel)) { + NS_WARNING("Failed to open channel!"); + return 1; + } + + if (!env->DefineSendCommand(&testShellParent)) { + NS_WARNING("DefineChildObject failed!"); + return 1; + } + + const char* filename = argc > 1 ? argv[1] : nsnull; + env->Process(filename); + + return env->ExitCode(); +} + +struct TestShellData { + int* result; + int argc; + char** argv; +}; + +void +TestShellMainWrapper(void* aData) +{ + NS_ASSERTION(aData, "Don't give me a null pointer!"); + TestShellData& testShellData = *static_cast<TestShellData*>(aData); + + *testShellData.result = + TestShellMain(testShellData.argc, testShellData.argv); +} + } /* anonymous namespace */ -nsresult -XRE_LaunchChildProcess() +int +XRE_RunTestShell(int aArgc, char* aArgv[]) { - MessageLoop* ioLoop = - BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO); - NS_ENSURE_TRUE(ioLoop, NS_ERROR_NOT_INITIALIZED); + int result; + + TestShellData data = { &result, aArgc, aArgv }; - ioLoop->PostTask(FROM_HERE, new CreateChildProcess()); - return NS_OK; + nsresult rv = + XRE_InitParentProcess(aArgc, aArgv, TestShellMainWrapper, &data); + NS_ENSURE_SUCCESS(rv, 1); + + return result; }
--- a/xpcom/build/nsXULAppAPI.h +++ b/xpcom/build/nsXULAppAPI.h @@ -419,26 +419,27 @@ XRE_API(void, XRE_FreeAppData, (nsXREAppData *aAppData)) enum GeckoChildProcessType { GeckoChildProcess_Default, GeckoChildProcess_Plugin, GeckoChildProcess_Tab, + GeckoChildProcess_TestShell, GeckoChildProcess_End, GeckoChildProcess_Invalid = GeckoChildProcess_End }; static const char* const kGeckoChildProcessTypeString[] = { "default", "plugin", "tab", - 0 + "testshell" }; XRE_API(const char*, XRE_ChildProcessTypeToString, (GeckoChildProcessType aProcessType)) XRE_API(GeckoChildProcessType, XRE_StringToChildProcessType, (const char* aProcessTypeString)) @@ -450,12 +451,13 @@ XRE_API(nsresult, typedef void (*MainFunction)(void* aData); XRE_API(nsresult, XRE_InitParentProcess, (int aArgc, char* aArgv[], MainFunction aMainFunction, void* aMainFunctionExtraData)) -XRE_API(nsresult, - XRE_LaunchChildProcess, ()) +XRE_API(int, + XRE_RunTestShell, (int aArgc, + char* aArgv[])) #endif // _nsXULAppAPI_h__