1 // Copyright 2012 the V8 project authors. All rights reserved.
3 // Copyright (c) 2013 Intel Corporation. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
8 // This is the XWalk Extensions Shell. It implements a simple javascript shell,
9 // based on V8, that can load XWalk Extensions for testing purposes.
10 // It is a single process application which runs with three threads (main, IO
12 // The overall implementation started upon v8/samples/shell.cc .
16 #include "base/at_exit.h"
17 #include "base/command_line.h"
18 #include "base/file_util.h"
19 #include "base/files/file_path.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/message_loop/message_loop.h"
22 #include "base/message_loop/message_pump_libevent.h"
23 #include "base/run_loop.h"
24 #include "base/synchronization/waitable_event.h"
25 #include "base/task_runner_util.h"
26 #include "base/threading/thread.h"
27 #include "ipc/ipc_sync_channel.h"
28 #include "xwalk/extensions/common/xwalk_extension_server.h"
29 #include "xwalk/extensions/common/xwalk_extension_switches.h"
30 #include "xwalk/extensions/xesh/xesh_v8_runner.h"
33 using xwalk::extensions::XWalkExtensionServer;
35 // Specifies which file XESh will use as input.
36 const char kInputFilePath[] = "input-file";
40 inline void PrintInitialInfo() {
41 fprintf(stderr, "\n---- XESh: XWalk Extensions Shell ----");
42 fprintf(stderr, "\nCrosswalk Version: %s\nv8 Version: %s\n", XWALK_VERSION,
43 XEShV8Runner::GetV8Version());
46 inline void PrintPromptLine() {
47 fprintf(stderr, "xesh> ");
50 std::string ReadLine() {
52 static const size_t kBufferSize = 256;
53 char buffer[kBufferSize];
55 while (fgets(buffer, kBufferSize - 1, stdin) != NULL) {
57 size_t len = result.length();
60 char end = result[len - 1];
61 if (end == '\n' || end == '\0')
72 // This class will watch the stdin file descriptor (i.e. stdin) so we know
73 // when we can read without blocking its thread. It will live on the IO Thread.
74 // In case where XESh got executed with the parameter --input-file=
75 // this class will first read this input file, get its content executed by v8
76 // and then it will start listening to stdin again.
77 class InputWatcher : public base::MessagePumpLibevent::Watcher {
79 InputWatcher(XEShV8Runner* v8_runner, base::MessageLoop* v8_loop)
80 : is_waiting_v8_runner_(false),
81 v8_runner_(v8_runner),
82 v8_message_loop_(v8_loop) {}
84 virtual ~InputWatcher() {}
86 // base::MessagePumpLibevent::Watcher implementation.
87 void virtual OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
88 if (is_waiting_v8_runner_)
91 CallV8ExecuteString(ReadLine());
94 void virtual OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
96 void StartWatching() {
97 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
99 if (cmd_line->HasSwitch(kInputFilePath)) {
100 std::string file_contents;
101 ReadFileToString(cmd_line->GetSwitchValuePath(kInputFilePath),
104 CallV8ExecuteString(file_contents);
107 // base::MessageLoop::current() will return the IO thread message loop.
108 base::MessageLoopForIO* io_loop =
109 static_cast<base::MessageLoopForIO*>(base::MessageLoop::current());
110 io_loop->WatchFileDescriptor(STDIN_FILENO, true,
111 base::MessageLoopForIO::WATCH_READ, &fd_watcher_, this);
115 void CallV8ExecuteString(std::string statement) {
116 is_waiting_v8_runner_ = true;
118 PostTaskAndReplyWithResult(v8_message_loop_->message_loop_proxy(),
120 base::Bind(&XEShV8Runner::ExecuteString, base::Unretained(v8_runner_),
122 base::Bind(&InputWatcher::OnFinishedV8Execution,
123 base::Unretained(this)));
126 void OnFinishedV8Execution(const std::string& result) {
127 is_waiting_v8_runner_ = false;
129 fprintf(stderr, "%s\n", result.c_str());
133 bool is_waiting_v8_runner_;
134 XEShV8Runner* v8_runner_;
135 base::MessageLoop* v8_message_loop_;
136 base::MessagePumpLibevent::FileDescriptorWatcher fd_watcher_;
138 DISALLOW_COPY_AND_ASSIGN(InputWatcher);
141 // Creates and manages the lifetime of the native side of XWalkExtension's
142 // Framework. That means managing XWalkExtensionServer and its IPC-related
144 class ExtensionManager {
147 : shutdown_event_(false, false) {
150 ~ExtensionManager() {
151 shutdown_event_.Signal();
154 void LoadExtensions() {
155 base::FilePath extensions_dir =
156 CommandLine::ForCurrentProcess()->GetSwitchValuePath(
157 switches::kXWalkExternalExtensionsPath);
159 scoped_ptr<base::ValueMap> runtime_variables(new base::ValueMap);
160 (*runtime_variables)["app_id"] = new base::StringValue("xesh");
162 std::vector<std::string> extensions =
163 RegisterExternalExtensionsInDirectory(&server_, extensions_dir,
164 runtime_variables.Pass());
166 fprintf(stderr, "\nExtensions Loaded:\n");
167 std::vector<std::string>::const_iterator it = extensions.begin();
168 for (; it != extensions.end(); ++it)
169 fprintf(stderr, "- %s\n", it->c_str());
172 void Initialize(base::MessageLoopProxy* io_message_loop_proxy) {
173 handle_ = IPC::Channel::GenerateVerifiedChannelID(std::string());
175 server_channel_ = IPC::SyncChannel::Create(handle_,
176 IPC::Channel::MODE_SERVER, &server_, io_message_loop_proxy, true,
179 server_.Initialize(server_channel_.get());
182 const IPC::ChannelHandle& ipc_channel_handle() { return handle_; }
185 IPC::ChannelHandle handle_;
186 base::WaitableEvent shutdown_event_;
187 XWalkExtensionServer server_;
188 scoped_ptr<IPC::SyncChannel> server_channel_;
192 int main(int argc, char* argv[]) {
193 base::AtExitManager exit_manager;
194 CommandLine::Init(argc, argv);
198 base::MessageLoop main_message_loop(base::MessageLoop::TYPE_UI);
199 main_message_loop.set_thread_name("XESh_Main");
201 base::Thread io_thread("XESh_IOThread");
202 io_thread.StartWithOptions(base::Thread::Options(base::MessageLoop::TYPE_IO,
205 base::Thread v8_thread("XESh_V8Thread");
206 v8_thread.StartWithOptions(base::Thread::Options(
207 base::MessageLoop::TYPE_DEFAULT, 0));
209 ExtensionManager extension_manager;
210 extension_manager.LoadExtensions();
211 extension_manager.Initialize(io_thread.message_loop_proxy());
213 XEShV8Runner v8_runner;
214 static_cast<base::MessageLoopForIO*>(v8_thread.message_loop())->PostTask(
215 FROM_HERE, base::Bind(&XEShV8Runner::Initialize,
216 base::Unretained(&v8_runner), argc, argv, io_thread.message_loop_proxy(),
217 extension_manager.ipc_channel_handle()));
219 InputWatcher input_watcher(&v8_runner, v8_thread.message_loop());
221 static_cast<base::MessageLoopForIO*>(io_thread.message_loop())->PostTask(
222 FROM_HERE, base::Bind(&InputWatcher::StartWatching,
223 base::Unretained(&input_watcher)));
226 base::RunLoop run_loop;
229 static_cast<base::MessageLoopForIO*>(v8_thread.message_loop())->PostTask(
230 FROM_HERE, base::Bind(&XEShV8Runner::Shutdown,
231 base::Unretained(&v8_runner)));