Initial Implementation of Node prelaunch
[platform/framework/web/crosswalk-tizen.git] / atom / browser / atom_browser_main_parts.cc
1 // Copyright (c) 2013 GitHub, Inc.
2 // Use of this source code is governed by the MIT license that can be
3 // found in the LICENSE file.
4
5 #include "atom/browser/atom_browser_main_parts.h"
6
7 #include "atom/browser/api/trackable_object.h"
8 #include "atom/browser/atom_access_token_store.h"
9 #include "atom/browser/atom_browser_client.h"
10 #include "atom/browser/atom_browser_context.h"
11 #include "atom/browser/atom_web_ui_controller_factory.h"
12 #include "atom/browser/bridge_task_runner.h"
13 #include "atom/browser/browser.h"
14 #include "atom/browser/javascript_environment.h"
15 #include "atom/browser/node_debugger.h"
16 #include "atom/common/api/atom_bindings.h"
17 #include "atom/common/asar/asar_util.h"
18 #include "atom/common/node_bindings.h"
19 #include "atom/common/node_includes.h"
20 #include "base/command_line.h"
21 #include "base/feature_list.h"
22 #include "base/threading/thread_task_runner_handle.h"
23 #include "chrome/browser/browser_process.h"
24 #include "content/public/browser/child_process_security_policy.h"
25 #include "device/geolocation/geolocation_delegate.h"
26 #include "device/geolocation/geolocation_provider.h"
27 #include "v8/include/v8-debug.h"
28
29 #include "gin/v8_initializer.h"
30
31 #if defined(USE_X11)
32 #include "chrome/browser/ui/libgtkui/gtk_util.h"
33 #include "ui/events/devices/x11/touch_factory_x11.h"
34 #endif
35
36 #if defined(USE_EFL)
37 #include "ui/display/screen_efl.h"
38 #endif
39
40 #include "atom/common/node_includes.h"
41
42 namespace atom {
43
44 namespace {
45
46 // A provider of Geolocation services to override AccessTokenStore.
47 class AtomGeolocationDelegate : public device::GeolocationDelegate {
48  public:
49   AtomGeolocationDelegate() = default;
50
51   scoped_refptr<device::AccessTokenStore> CreateAccessTokenStore() final {
52     return new AtomAccessTokenStore();
53   }
54
55  private:
56   DISALLOW_COPY_AND_ASSIGN(AtomGeolocationDelegate);
57 };
58
59 template<typename T>
60 void Erase(T* container, typename T::iterator iter) {
61   container->erase(iter);
62 }
63
64 }  // namespace
65
66 // static
67 bool g_prelaunch = false;
68 AtomBrowserMainParts* AtomBrowserMainParts::self_ = nullptr;
69 node::Environment* env_ = nullptr;
70 scoped_refptr<BridgeTaskRunner> self_bridge_task_runner;
71 std::unique_ptr<Browser> self_browser;
72 std::unique_ptr<JavascriptEnvironment> self_js_env;
73 std::unique_ptr<NodeBindings> self_node_bindings;
74 std::unique_ptr<AtomBindings> self_atom_bindings;
75 std::unique_ptr<NodeEnvironment> self_node_env;
76 std::unique_ptr<NodeDebugger> self_node_debugger;
77
78 AtomBrowserMainParts::AtomBrowserMainParts()
79     : fake_browser_process_(new BrowserProcess),
80       exit_code_(nullptr),
81       gc_timer_(true, true) {
82   DCHECK(!self_) << "Cannot have two AtomBrowserMainParts";
83   if (g_prelaunch) {
84     browser_ = std::move(self_browser);
85     node_bindings_ = std::move(self_node_bindings);
86     atom_bindings_ = std::move(self_atom_bindings);
87
88     js_env_ = std::move(self_js_env);
89     node_debugger_ = std::move(self_node_debugger);
90     bridge_task_runner_ = self_bridge_task_runner;
91     node_env_ = std::move(self_node_env);
92   } else {
93     browser_.reset(new Browser);
94     node_bindings_.reset(NodeBindings::Create(NodeBindings::BROWSER));
95     atom_bindings_.reset(new AtomBindings(uv_default_loop()));
96   }
97   self_ = this;
98   // Register extension scheme as web safe scheme.
99   content::ChildProcessSecurityPolicy::GetInstance()->
100       RegisterWebSafeScheme("chrome-extension");
101 }
102
103 AtomBrowserMainParts::~AtomBrowserMainParts() {
104   asar::ClearArchives();
105   // Leak the JavascriptEnvironment on exit.
106   // This is to work around the bug that V8 would be waiting for background
107   // tasks to finish on exit, while somehow it waits forever in Electron, more
108   // about this can be found at https://github.com/electron/electron/issues/4767.
109   // On the other handle there is actually no need to gracefully shutdown V8
110   // on exit in the main process, we already ensured all necessary resources get
111   // cleaned up, and it would make quitting faster.
112   ignore_result(js_env_.release());
113 }
114
115 // static
116 AtomBrowserMainParts* AtomBrowserMainParts::Get() {
117   DCHECK(self_);
118   return self_;
119 }
120
121 Browser* AtomBrowserMainParts::GetStaticBrowser() {
122   return self_browser.get();
123 }
124
125 void AtomBrowserMainParts::SetNodeEnvironment() {
126   if (g_prelaunch)
127     return;
128
129   std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
130   feature_list->InitializeFromCommandLine("", "");
131   base::FeatureList::SetInstance(std::move(feature_list));
132
133   gin::V8Initializer::LoadV8Snapshot();
134   gin::V8Initializer::LoadV8Natives();
135
136   self_browser.reset(new atom::Browser);
137   self_bridge_task_runner = new atom::BridgeTaskRunner;
138   base::ThreadTaskRunnerHandle handle(self_bridge_task_runner);
139
140   self_js_env.reset(new atom::JavascriptEnvironment);
141   self_node_bindings.reset(atom::NodeBindings::Create(atom::NodeBindings::BROWSER));
142   self_atom_bindings.reset(new atom::AtomBindings(uv_default_loop()));
143   self_node_bindings->Initialize();
144
145   self_node_debugger.reset(new atom::NodeDebugger(self_js_env->isolate()));
146   env_ = self_node_bindings->CreateEnvironment(self_js_env->context());
147   self_node_env.reset(new atom::NodeEnvironment(env_));
148
149   if (self_node_debugger->IsRunning())
150     env_->AssignToContext(v8::Debug::GetDebugContext(self_js_env->isolate()));
151
152   self_atom_bindings->BindTo(self_js_env->isolate(), env_->process_object());
153
154   self_node_bindings->LoadEnvironment(env_);
155   self_node_bindings->set_uv_env(env_);
156   g_prelaunch = true;
157 }
158
159 bool AtomBrowserMainParts::SetExitCode(int code) {
160   if (!exit_code_)
161     return false;
162
163   *exit_code_ = code;
164   return true;
165 }
166
167 int AtomBrowserMainParts::GetExitCode() {
168   return exit_code_ != nullptr ? *exit_code_ : 0;
169 }
170
171 base::Closure AtomBrowserMainParts::RegisterDestructionCallback(
172     const base::Closure& callback) {
173   auto iter = destructors_.insert(destructors_.end(), callback);
174   return base::Bind(&Erase<std::list<base::Closure>>, &destructors_, iter);
175 }
176
177 void AtomBrowserMainParts::PreEarlyInitialization() {
178   brightray::BrowserMainParts::PreEarlyInitialization();
179 #if defined(OS_POSIX)
180   HandleSIGCHLD();
181 #endif
182 }
183
184 void AtomBrowserMainParts::PostEarlyInitialization() {
185   brightray::BrowserMainParts::PostEarlyInitialization();
186   if (g_prelaunch)
187       return;
188
189   // Temporary set the bridge_task_runner_ as current thread's task runner,
190   // so we can fool gin::PerIsolateData to use it as its task runner, instead
191   // of getting current message loop's task runner, which is null for now.
192   bridge_task_runner_ = new BridgeTaskRunner;
193   base::ThreadTaskRunnerHandle handle(bridge_task_runner_);
194
195   // The ProxyResolverV8 has setup a complete V8 environment, in order to
196   // avoid conflicts we only initialize our V8 environment after that.
197   js_env_.reset(new JavascriptEnvironment);
198
199   node_bindings_->Initialize();
200
201   // Support the "--debug" switch.
202   node_debugger_.reset(new NodeDebugger(js_env_->isolate()));
203
204   // Create the global environment.
205   node::Environment* env =
206       node_bindings_->CreateEnvironment(js_env_->context());
207   node_env_.reset(new NodeEnvironment(env));
208
209   // Make sure node can get correct environment when debugging.
210   if (node_debugger_->IsRunning())
211     env->AssignToContext(v8::Debug::GetDebugContext(js_env_->isolate()));
212
213   // Add Electron extended APIs.
214   atom_bindings_->BindTo(js_env_->isolate(), env->process_object());
215
216   // Load everything.
217   node_bindings_->LoadEnvironment(env);
218
219   // Wrap the uv loop with global env.
220   node_bindings_->set_uv_env(env);
221 }
222
223 void AtomBrowserMainParts::PreMainMessageLoopRun() {
224   js_env_->OnMessageLoopCreated();
225
226   // Run user's main script before most things get initialized, so we can have
227   // a chance to setup everything.
228   node_bindings_->PrepareMessageLoop();
229   node_bindings_->RunMessageLoop();
230
231 #if defined(USE_X11)
232   ui::TouchFactory::SetTouchDeviceListFromCommandLine();
233 #endif
234
235   // Start idle gc.
236   gc_timer_.Start(
237       FROM_HERE, base::TimeDelta::FromMinutes(1),
238       base::Bind(&v8::Isolate::LowMemoryNotification,
239                  base::Unretained(js_env_->isolate())));
240
241   content::WebUIControllerFactory::RegisterFactory(
242       AtomWebUIControllerFactory::GetInstance());
243
244   brightray::BrowserMainParts::PreMainMessageLoopRun();
245   bridge_task_runner_->MessageLoopIsReady();
246   bridge_task_runner_ = nullptr;
247
248 #if defined(USE_X11)
249   libgtkui::GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess());
250 #endif
251
252 #if defined(USE_EFL)
253   ui::InstallScreenInstance();
254 #endif
255
256 #if !defined(OS_MACOSX)
257   // The corresponding call in macOS is in AtomApplicationDelegate.
258   Browser::Get()->WillFinishLaunching();
259   std::unique_ptr<base::DictionaryValue> empty_info(new base::DictionaryValue);
260   Browser::Get()->DidFinishLaunching(*empty_info);
261 #endif
262 }
263
264 bool AtomBrowserMainParts::MainMessageLoopRun(int* result_code) {
265   exit_code_ = result_code;
266   return brightray::BrowserMainParts::MainMessageLoopRun(result_code);
267 }
268
269 void AtomBrowserMainParts::PostMainMessageLoopStart() {
270   brightray::BrowserMainParts::PostMainMessageLoopStart();
271 #if defined(OS_POSIX)
272   HandleShutdownSignals();
273 #endif
274   device::GeolocationProvider::SetGeolocationDelegate(
275       new AtomGeolocationDelegate());
276 }
277
278 void AtomBrowserMainParts::PostMainMessageLoopRun() {
279   brightray::BrowserMainParts::PostMainMessageLoopRun();
280
281   js_env_->OnMessageLoopDestroying();
282
283 #if defined(OS_MACOSX)
284   FreeAppDelegate();
285 #endif
286
287   // Make sure destruction callbacks are called before message loop is
288   // destroyed, otherwise some objects that need to be deleted on IO thread
289   // won't be freed.
290   // We don't use ranged for loop because iterators are getting invalided when
291   // the callback runs.
292   for (auto iter = destructors_.begin(); iter != destructors_.end();) {
293     base::Closure& callback = *iter;
294     ++iter;
295     callback.Run();
296   }
297 }
298
299 }  // namespace atom