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.
5 #include "atom/browser/atom_browser_main_parts.h"
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"
29 #include "gin/v8_initializer.h"
32 #include "chrome/browser/ui/libgtkui/gtk_util.h"
33 #include "ui/events/devices/x11/touch_factory_x11.h"
37 #include "ui/display/screen_efl.h"
40 #include "atom/common/node_includes.h"
46 // A provider of Geolocation services to override AccessTokenStore.
47 class AtomGeolocationDelegate : public device::GeolocationDelegate {
49 AtomGeolocationDelegate() = default;
51 scoped_refptr<device::AccessTokenStore> CreateAccessTokenStore() final {
52 return new AtomAccessTokenStore();
56 DISALLOW_COPY_AND_ASSIGN(AtomGeolocationDelegate);
60 void Erase(T* container, typename T::iterator iter) {
61 container->erase(iter);
64 // static - lines have been moved to limit their visibility only to this file
65 bool g_prelaunch = false;
66 node::Environment* env_ = nullptr;
67 scoped_refptr<BridgeTaskRunner> self_bridge_task_runner;
68 std::unique_ptr<Browser> self_browser;
69 std::unique_ptr<JavascriptEnvironment> self_js_env;
70 std::unique_ptr<NodeBindings> self_node_bindings;
71 std::unique_ptr<AtomBindings> self_atom_bindings;
72 std::unique_ptr<NodeEnvironment> self_node_env;
73 std::unique_ptr<NodeDebugger> self_node_debugger;
77 AtomBrowserMainParts* AtomBrowserMainParts::self_ = nullptr;
79 AtomBrowserMainParts::AtomBrowserMainParts()
80 : fake_browser_process_(new BrowserProcess),
82 gc_timer_(true, true) {
83 DCHECK(!self_) << "Cannot have two AtomBrowserMainParts";
85 browser_ = std::move(self_browser);
86 node_bindings_ = std::move(self_node_bindings);
87 atom_bindings_ = std::move(self_atom_bindings);
89 js_env_ = std::move(self_js_env);
90 js_env_->SetupLocker();
91 node_debugger_ = std::move(self_node_debugger);
92 bridge_task_runner_ = self_bridge_task_runner;
93 node_env_ = std::move(self_node_env);
95 browser_.reset(new Browser);
96 node_bindings_.reset(NodeBindings::Create(NodeBindings::BROWSER));
97 atom_bindings_.reset(new AtomBindings(node_bindings_->uv_loop()));
100 // Register extension scheme as web safe scheme.
101 content::ChildProcessSecurityPolicy::GetInstance()->
102 RegisterWebSafeScheme("chrome-extension");
105 AtomBrowserMainParts::~AtomBrowserMainParts() {
106 asar::ClearArchives();
107 // Leak the JavascriptEnvironment on exit.
108 // This is to work around the bug that V8 would be waiting for background
109 // tasks to finish on exit, while somehow it waits forever in Electron, more
110 // about this can be found at https://github.com/electron/electron/issues/4767.
111 // On the other handle there is actually no need to gracefully shutdown V8
112 // on exit in the main process, we already ensured all necessary resources get
113 // cleaned up, and it would make quitting faster.
114 ignore_result(js_env_.release());
118 AtomBrowserMainParts* AtomBrowserMainParts::Get() {
123 Browser* AtomBrowserMainParts::GetStaticBrowser() {
124 return self_browser.get();
127 void AtomBrowserMainParts::SetNodeEnvironment() {
131 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
132 feature_list->InitializeFromCommandLine("", "");
133 base::FeatureList::SetInstance(std::move(feature_list));
135 gin::V8Initializer::LoadV8Snapshot();
136 gin::V8Initializer::LoadV8Natives();
138 self_browser.reset(new atom::Browser);
139 self_bridge_task_runner = new atom::BridgeTaskRunner;
140 base::ThreadTaskRunnerHandle handle(self_bridge_task_runner);
142 self_js_env.reset(new atom::JavascriptEnvironment);
143 self_node_bindings.reset(atom::NodeBindings::Create(atom::NodeBindings::BROWSER));
144 self_atom_bindings.reset(new atom::AtomBindings(self_node_bindings->uv_loop()));
145 self_node_bindings->Initialize();
147 self_node_debugger.reset(new atom::NodeDebugger(self_js_env->isolate(), self_node_bindings->uv_loop()));
148 env_ = self_node_bindings->CreateEnvironment(self_js_env->context());
149 self_node_env.reset(new atom::NodeEnvironment(env_));
151 if (self_node_debugger->IsRunning())
152 env_->AssignToContext(v8::Debug::GetDebugContext(self_js_env->isolate()));
154 self_atom_bindings->BindTo(self_js_env->isolate(), env_->process_object());
156 self_node_bindings->LoadEnvironment(env_);
157 self_node_bindings->set_uv_env(env_);
161 bool AtomBrowserMainParts::SetExitCode(int code) {
169 int AtomBrowserMainParts::GetExitCode() {
170 return exit_code_ != nullptr ? *exit_code_ : 0;
173 base::Closure AtomBrowserMainParts::RegisterDestructionCallback(
174 const base::Closure& callback) {
175 auto iter = destructors_.insert(destructors_.end(), callback);
176 return base::Bind(&Erase<std::list<base::Closure>>, &destructors_, iter);
179 void AtomBrowserMainParts::PreEarlyInitialization() {
180 brightray::BrowserMainParts::PreEarlyInitialization();
181 #if defined(OS_POSIX)
186 void AtomBrowserMainParts::PostEarlyInitialization() {
187 brightray::BrowserMainParts::PostEarlyInitialization();
191 // Temporary set the bridge_task_runner_ as current thread's task runner,
192 // so we can fool gin::PerIsolateData to use it as its task runner, instead
193 // of getting current message loop's task runner, which is null for now.
194 bridge_task_runner_ = new BridgeTaskRunner;
195 base::ThreadTaskRunnerHandle handle(bridge_task_runner_);
197 // The ProxyResolverV8 has setup a complete V8 environment, in order to
198 // avoid conflicts we only initialize our V8 environment after that.
199 js_env_.reset(new JavascriptEnvironment);
200 js_env_->SetupLocker();
202 node_bindings_->Initialize();
204 // Support the "--debug" switch.
205 node_debugger_.reset(new NodeDebugger(js_env_->isolate(), node_bindings_->uv_loop()));
207 // Create the global environment.
208 node::Environment* env =
209 node_bindings_->CreateEnvironment(js_env_->context());
210 node_env_.reset(new NodeEnvironment(env));
212 // Make sure node can get correct environment when debugging.
213 if (node_debugger_->IsRunning())
214 env->AssignToContext(v8::Debug::GetDebugContext(js_env_->isolate()));
216 // Add Electron extended APIs.
217 atom_bindings_->BindTo(js_env_->isolate(), env->process_object());
220 node_bindings_->LoadEnvironment(env);
222 // Wrap the uv loop with global env.
223 node_bindings_->set_uv_env(env);
226 void AtomBrowserMainParts::PreMainMessageLoopRun() {
227 js_env_->OnMessageLoopCreated();
229 // Run user's main script before most things get initialized, so we can have
230 // a chance to setup everything.
231 node_bindings_->PrepareMessageLoop();
232 node_bindings_->RunMessageLoop();
235 ui::TouchFactory::SetTouchDeviceListFromCommandLine();
240 FROM_HERE, base::TimeDelta::FromMinutes(1),
241 base::Bind(&v8::Isolate::LowMemoryNotification,
242 base::Unretained(js_env_->isolate())));
244 content::WebUIControllerFactory::RegisterFactory(
245 AtomWebUIControllerFactory::GetInstance());
247 brightray::BrowserMainParts::PreMainMessageLoopRun();
248 bridge_task_runner_->MessageLoopIsReady();
249 bridge_task_runner_ = nullptr;
252 libgtkui::GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess());
256 ui::InstallScreenInstance();
259 #if !defined(OS_MACOSX)
260 // The corresponding call in macOS is in AtomApplicationDelegate.
261 Browser::Get()->WillFinishLaunching();
262 std::unique_ptr<base::DictionaryValue> empty_info(new base::DictionaryValue);
263 Browser::Get()->DidFinishLaunching(*empty_info);
267 bool AtomBrowserMainParts::MainMessageLoopRun(int* result_code) {
268 exit_code_ = result_code;
269 return brightray::BrowserMainParts::MainMessageLoopRun(result_code);
272 void AtomBrowserMainParts::PostMainMessageLoopStart() {
273 brightray::BrowserMainParts::PostMainMessageLoopStart();
274 #if defined(OS_POSIX)
275 HandleShutdownSignals();
277 device::GeolocationProvider::SetGeolocationDelegate(
278 new AtomGeolocationDelegate());
281 void AtomBrowserMainParts::PostMainMessageLoopRun() {
282 brightray::BrowserMainParts::PostMainMessageLoopRun();
284 js_env_->OnMessageLoopDestroying();
286 #if defined(OS_MACOSX)
290 // Make sure destruction callbacks are called before message loop is
291 // destroyed, otherwise some objects that need to be deleted on IO thread
293 // We don't use ranged for loop because iterators are getting invalided when
294 // the callback runs.
295 for (auto iter = destructors_.begin(); iter != destructors_.end();) {
296 base::Closure& callback = *iter;