// Copyright (c) 2013 GitHub, Inc. // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. #include "shell/browser/api/electron_api_power_monitor.h" #include "base/power_monitor/power_monitor.h" #include "base/power_monitor/power_monitor_device_source.h" #include "base/power_monitor/power_observer.h" #include "gin/data_object_builder.h" #include "gin/handle.h" #include "shell/browser/browser.h" #include "shell/browser/javascript_environment.h" #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/object_template_builder.h" #include "shell/common/node_includes.h" namespace gin { template <> struct Converter { static v8::Local ToV8(v8::Isolate* isolate, const ui::IdleState& in) { switch (in) { case ui::IDLE_STATE_ACTIVE: return StringToV8(isolate, "active"); case ui::IDLE_STATE_IDLE: return StringToV8(isolate, "idle"); case ui::IDLE_STATE_LOCKED: return StringToV8(isolate, "locked"); case ui::IDLE_STATE_UNKNOWN: default: return StringToV8(isolate, "unknown"); } } }; template <> struct Converter { static v8::Local ToV8( v8::Isolate* isolate, const base::PowerThermalObserver::DeviceThermalState& in) { switch (in) { case base::PowerThermalObserver::DeviceThermalState::kUnknown: return StringToV8(isolate, "unknown"); case base::PowerThermalObserver::DeviceThermalState::kNominal: return StringToV8(isolate, "nominal"); case base::PowerThermalObserver::DeviceThermalState::kFair: return StringToV8(isolate, "fair"); case base::PowerThermalObserver::DeviceThermalState::kSerious: return StringToV8(isolate, "serious"); case base::PowerThermalObserver::DeviceThermalState::kCritical: return StringToV8(isolate, "critical"); } } }; } // namespace gin namespace electron::api { gin::WrapperInfo PowerMonitor::kWrapperInfo = {gin::kEmbedderNativeGin}; PowerMonitor::PowerMonitor(v8::Isolate* isolate) { #if BUILDFLAG(IS_MAC) Browser::Get()->SetShutdownHandler(base::BindRepeating( &PowerMonitor::ShouldShutdown, base::Unretained(this))); #endif base::PowerMonitor::AddPowerStateObserver(this); base::PowerMonitor::AddPowerSuspendObserver(this); base::PowerMonitor::AddPowerThermalObserver(this); #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) InitPlatformSpecificMonitors(); #endif } PowerMonitor::~PowerMonitor() { base::PowerMonitor::RemovePowerStateObserver(this); base::PowerMonitor::RemovePowerSuspendObserver(this); base::PowerMonitor::RemovePowerThermalObserver(this); } bool PowerMonitor::ShouldShutdown() { return !Emit("shutdown"); } void PowerMonitor::OnPowerStateChange(bool on_battery_power) { if (on_battery_power) Emit("on-battery"); else Emit("on-ac"); } void PowerMonitor::OnSuspend() { Emit("suspend"); } void PowerMonitor::OnResume() { Emit("resume"); } void PowerMonitor::OnThermalStateChange(DeviceThermalState new_state) { v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope scope(isolate); EmitWithoutEvent( "thermal-state-change", gin::DataObjectBuilder(isolate).Set("state", new_state).Build()); } void PowerMonitor::OnSpeedLimitChange(int speed_limit) { v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); v8::HandleScope scope(isolate); EmitWithoutEvent( "speed-limit-change", gin::DataObjectBuilder(isolate).Set("limit", speed_limit).Build()); } #if BUILDFLAG(IS_LINUX) void PowerMonitor::SetListeningForShutdown(bool is_listening) { if (is_listening) { // unretained is OK because we own power_observer_linux_ power_observer_linux_.SetShutdownHandler(base::BindRepeating( &PowerMonitor::ShouldShutdown, base::Unretained(this))); } else { power_observer_linux_.SetShutdownHandler(base::RepeatingCallback()); } } #endif // static v8::Local PowerMonitor::Create(v8::Isolate* isolate) { auto* pm = new PowerMonitor(isolate); auto handle = gin::CreateHandle(isolate, pm).ToV8(); pm->Pin(isolate); return handle; } gin::ObjectTemplateBuilder PowerMonitor::GetObjectTemplateBuilder( v8::Isolate* isolate) { auto builder = gin_helper::EventEmitterMixin::GetObjectTemplateBuilder( isolate); #if BUILDFLAG(IS_LINUX) builder.SetMethod("setListeningForShutdown", &PowerMonitor::SetListeningForShutdown); #endif return builder; } const char* PowerMonitor::GetTypeName() { return "PowerMonitor"; } } // namespace electron::api namespace { using electron::api::PowerMonitor; ui::IdleState GetSystemIdleState(v8::Isolate* isolate, int idle_threshold) { if (idle_threshold > 0) { return ui::CalculateIdleState(idle_threshold); } else { isolate->ThrowException(v8::Exception::TypeError(gin::StringToV8( isolate, "Invalid idle threshold, must be greater than 0"))); return ui::IDLE_STATE_UNKNOWN; } } int GetSystemIdleTime() { return ui::CalculateIdleTime(); } bool IsOnBatteryPower() { return base::PowerMonitor::IsOnBatteryPower(); } base::PowerThermalObserver::DeviceThermalState GetCurrentThermalState() { return base::PowerMonitor::GetCurrentThermalState(); } void Initialize(v8::Local exports, v8::Local unused, v8::Local context, void* priv) { v8::Isolate* isolate = context->GetIsolate(); gin_helper::Dictionary dict(isolate, exports); dict.SetMethod("createPowerMonitor", base::BindRepeating(&PowerMonitor::Create)); dict.SetMethod("getSystemIdleState", base::BindRepeating(&GetSystemIdleState)); dict.SetMethod("getCurrentThermalState", base::BindRepeating(&GetCurrentThermalState)); dict.SetMethod("getSystemIdleTime", base::BindRepeating(&GetSystemIdleTime)); dict.SetMethod("isOnBatteryPower", base::BindRepeating(&IsOnBatteryPower)); } } // namespace NODE_LINKED_BINDING_CONTEXT_AWARE(electron_browser_power_monitor, Initialize)