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);
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;
78 AtomBrowserMainParts::AtomBrowserMainParts()
79 : fake_browser_process_(new BrowserProcess),
81 gc_timer_(true, true) {
82 DCHECK(!self_) << "Cannot have two AtomBrowserMainParts";
84 browser_ = std::move(self_browser);
85 node_bindings_ = std::move(self_node_bindings);
86 atom_bindings_ = std::move(self_atom_bindings);
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);
93 browser_.reset(new Browser);
94 node_bindings_.reset(NodeBindings::Create(NodeBindings::BROWSER));
95 atom_bindings_.reset(new AtomBindings(uv_default_loop()));
98 // Register extension scheme as web safe scheme.
99 content::ChildProcessSecurityPolicy::GetInstance()->
100 RegisterWebSafeScheme("chrome-extension");
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());
116 AtomBrowserMainParts* AtomBrowserMainParts::Get() {
121 Browser* AtomBrowserMainParts::GetStaticBrowser() {
122 return self_browser.get();
125 void AtomBrowserMainParts::SetNodeEnvironment() {
129 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
130 feature_list->InitializeFromCommandLine("", "");
131 base::FeatureList::SetInstance(std::move(feature_list));
133 gin::V8Initializer::LoadV8Snapshot();
134 gin::V8Initializer::LoadV8Natives();
136 self_browser.reset(new atom::Browser);
137 self_bridge_task_runner = new atom::BridgeTaskRunner;
138 base::ThreadTaskRunnerHandle handle(self_bridge_task_runner);
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();
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_));
149 if (self_node_debugger->IsRunning())
150 env_->AssignToContext(v8::Debug::GetDebugContext(self_js_env->isolate()));
152 self_atom_bindings->BindTo(self_js_env->isolate(), env_->process_object());
154 self_node_bindings->LoadEnvironment(env_);
155 self_node_bindings->set_uv_env(env_);
159 bool AtomBrowserMainParts::SetExitCode(int code) {
167 int AtomBrowserMainParts::GetExitCode() {
168 return exit_code_ != nullptr ? *exit_code_ : 0;
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);
177 void AtomBrowserMainParts::PreEarlyInitialization() {
178 brightray::BrowserMainParts::PreEarlyInitialization();
179 #if defined(OS_POSIX)
184 void AtomBrowserMainParts::PostEarlyInitialization() {
185 brightray::BrowserMainParts::PostEarlyInitialization();
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_);
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);
199 node_bindings_->Initialize();
201 // Support the "--debug" switch.
202 node_debugger_.reset(new NodeDebugger(js_env_->isolate()));
204 // Create the global environment.
205 node::Environment* env =
206 node_bindings_->CreateEnvironment(js_env_->context());
207 node_env_.reset(new NodeEnvironment(env));
209 // Make sure node can get correct environment when debugging.
210 if (node_debugger_->IsRunning())
211 env->AssignToContext(v8::Debug::GetDebugContext(js_env_->isolate()));
213 // Add Electron extended APIs.
214 atom_bindings_->BindTo(js_env_->isolate(), env->process_object());
217 node_bindings_->LoadEnvironment(env);
219 // Wrap the uv loop with global env.
220 node_bindings_->set_uv_env(env);
223 void AtomBrowserMainParts::PreMainMessageLoopRun() {
224 js_env_->OnMessageLoopCreated();
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();
232 ui::TouchFactory::SetTouchDeviceListFromCommandLine();
237 FROM_HERE, base::TimeDelta::FromMinutes(1),
238 base::Bind(&v8::Isolate::LowMemoryNotification,
239 base::Unretained(js_env_->isolate())));
241 content::WebUIControllerFactory::RegisterFactory(
242 AtomWebUIControllerFactory::GetInstance());
244 brightray::BrowserMainParts::PreMainMessageLoopRun();
245 bridge_task_runner_->MessageLoopIsReady();
246 bridge_task_runner_ = nullptr;
249 libgtkui::GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess());
253 ui::InstallScreenInstance();
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);
264 bool AtomBrowserMainParts::MainMessageLoopRun(int* result_code) {
265 exit_code_ = result_code;
266 return brightray::BrowserMainParts::MainMessageLoopRun(result_code);
269 void AtomBrowserMainParts::PostMainMessageLoopStart() {
270 brightray::BrowserMainParts::PostMainMessageLoopStart();
271 #if defined(OS_POSIX)
272 HandleShutdownSignals();
274 device::GeolocationProvider::SetGeolocationDelegate(
275 new AtomGeolocationDelegate());
278 void AtomBrowserMainParts::PostMainMessageLoopRun() {
279 brightray::BrowserMainParts::PostMainMessageLoopRun();
281 js_env_->OnMessageLoopDestroying();
283 #if defined(OS_MACOSX)
287 // Make sure destruction callbacks are called before message loop is
288 // destroyed, otherwise some objects that need to be deleted on IO thread
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;