Browse Source

feat: Implementation of getGPUInfo API. (#13486)

* Implementation of getGPUInfo API.

* Clear promise set

* Changes to promise usage

* Minor fixes

* Fix linux build

* Update spec

* Fix lint (linter didn't run on windows locally)

* Test running single test for CI

* Update spec
Nitish Sakhawalkar 6 years ago
parent
commit
5c108728d6

+ 22 - 0
atom/browser/api/atom_api_app.cc

@@ -10,6 +10,7 @@
 #include "atom/browser/api/atom_api_menu.h"
 #include "atom/browser/api/atom_api_session.h"
 #include "atom/browser/api/atom_api_web_contents.h"
+#include "atom/browser/api/gpuinfo_manager.h"
 #include "atom/browser/atom_browser_context.h"
 #include "atom/browser/atom_browser_main_parts.h"
 #include "atom/browser/login_handler.h"
@@ -548,6 +549,7 @@ App::App(v8::Isolate* isolate) {
   static_cast<AtomBrowserClient*>(AtomBrowserClient::Get())->set_delegate(this);
   Browser::Get()->AddObserver(this);
   content::GpuDataManager::GetInstance()->AddObserver(this);
+
   base::ProcessId pid = base::GetCurrentProcId();
   auto process_metric = std::make_unique<atom::ProcessMetric>(
       content::PROCESS_TYPE_BROWSER, pid,
@@ -1148,6 +1150,25 @@ v8::Local<v8::Value> App::GetGPUFeatureStatus(v8::Isolate* isolate) {
   return mate::ConvertToV8(isolate, status ? *status : temp);
 }
 
+v8::Local<v8::Promise> App::GetGPUInfo(v8::Isolate* isolate,
+                                       const std::string& info_type) {
+  auto* const gpu_data_manager = content::GpuDataManagerImpl::GetInstance();
+  scoped_refptr<util::Promise> promise = new util::Promise(isolate);
+  if ((info_type != "basic" && info_type != "complete") ||
+      !gpu_data_manager->GpuAccessAllowed(nullptr)) {
+    promise->Reject("Error fetching GPU Info");
+    return promise->GetHandle();
+  }
+
+  auto* const info_mgr = GPUInfoManager::GetInstance();
+  if (info_type == "complete") {
+    info_mgr->FetchCompleteInfo(promise);
+  } else /* (info_type == "basic") */ {
+    info_mgr->FetchBasicInfo(promise);
+  }
+  return promise->GetHandle();
+}
+
 void App::EnableMixedSandbox(mate::Arguments* args) {
   if (Browser::Get()->is_ready()) {
     args->ThrowError(
@@ -1270,6 +1291,7 @@ void App::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("getFileIcon", &App::GetFileIcon)
       .SetMethod("getAppMetrics", &App::GetAppMetrics)
       .SetMethod("getGPUFeatureStatus", &App::GetGPUFeatureStatus)
+      .SetMethod("getGPUInfo", &App::GetGPUInfo)
 // TODO(juturu): Remove in 2.0, deprecate before then with warnings
 #if defined(OS_MACOSX)
       .SetMethod("moveToApplicationsFolder", &App::MoveToApplicationsFolder)

+ 2 - 0
atom/browser/api/atom_api_app.h

@@ -199,6 +199,8 @@ class App : public AtomBrowserClient::Delegate,
 
   std::vector<mate::Dictionary> GetAppMetrics(v8::Isolate* isolate);
   v8::Local<v8::Value> GetGPUFeatureStatus(v8::Isolate* isolate);
+  v8::Local<v8::Promise> GetGPUInfo(v8::Isolate* isolate,
+                                    const std::string& info_type);
   void EnableMixedSandbox(mate::Arguments* args);
 
 #if defined(OS_MACOSX)

+ 102 - 0
atom/browser/api/gpu_info_enumerator.cc

@@ -0,0 +1,102 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "atom/browser/api/gpu_info_enumerator.h"
+
+#include <utility>
+
+namespace atom {
+
+GPUInfoEnumerator::GPUInfoEnumerator()
+    : value_stack(), current(std::make_unique<base::DictionaryValue>()) {}
+
+GPUInfoEnumerator::~GPUInfoEnumerator() {}
+
+void GPUInfoEnumerator::AddInt64(const char* name, int64_t value) {
+  current->SetInteger(name, value);
+}
+
+void GPUInfoEnumerator::AddInt(const char* name, int value) {
+  current->SetInteger(name, value);
+}
+
+void GPUInfoEnumerator::AddString(const char* name, const std::string& value) {
+  if (!value.empty())
+    current->SetString(name, value);
+}
+
+void GPUInfoEnumerator::AddBool(const char* name, bool value) {
+  current->SetBoolean(name, value);
+}
+
+void GPUInfoEnumerator::AddTimeDeltaInSecondsF(const char* name,
+                                               const base::TimeDelta& value) {
+  current->SetInteger(name, value.InMilliseconds());
+}
+
+void GPUInfoEnumerator::BeginGPUDevice() {
+  value_stack.push(std::move(current));
+  current = std::make_unique<base::DictionaryValue>();
+}
+
+void GPUInfoEnumerator::EndGPUDevice() {
+  auto& top_value = value_stack.top();
+  // GPUDevice can be more than one. So create a list of all.
+  // The first one is the active GPU device.
+  if (top_value->HasKey(kGPUDeviceKey)) {
+    base::ListValue* list;
+    top_value->GetList(kGPUDeviceKey, &list);
+    list->Append(std::move(current));
+  } else {
+    auto gpus = std::make_unique<base::ListValue>();
+    gpus->Append(std::move(current));
+    top_value->SetList(kGPUDeviceKey, std::move(gpus));
+  }
+  current = std::move(top_value);
+  value_stack.pop();
+}
+
+void GPUInfoEnumerator::BeginVideoDecodeAcceleratorSupportedProfile() {
+  value_stack.push(std::move(current));
+  current = std::make_unique<base::DictionaryValue>();
+}
+
+void GPUInfoEnumerator::EndVideoDecodeAcceleratorSupportedProfile() {
+  auto& top_value = value_stack.top();
+  top_value->SetDictionary(kVideoDecodeAcceleratorSupportedProfileKey,
+                           std::move(current));
+  current = std::move(top_value);
+  value_stack.pop();
+}
+
+void GPUInfoEnumerator::BeginVideoEncodeAcceleratorSupportedProfile() {
+  value_stack.push(std::move(current));
+  current = std::make_unique<base::DictionaryValue>();
+}
+
+void GPUInfoEnumerator::EndVideoEncodeAcceleratorSupportedProfile() {
+  auto& top_value = value_stack.top();
+  top_value->SetDictionary(kVideoEncodeAcceleratorSupportedProfileKey,
+                           std::move(current));
+  current = std::move(top_value);
+  value_stack.pop();
+}
+
+void GPUInfoEnumerator::BeginAuxAttributes() {
+  value_stack.push(std::move(current));
+  current = std::make_unique<base::DictionaryValue>();
+}
+
+void GPUInfoEnumerator::EndAuxAttributes() {
+  auto& top_value = value_stack.top();
+  top_value->SetDictionary(kAuxAttributesKey, std::move(current));
+  current = std::move(top_value);
+  value_stack.pop();
+}
+
+std::unique_ptr<base::DictionaryValue> GPUInfoEnumerator::GetDictionary() {
+  return std::move(current);
+}
+
+}  // namespace atom

+ 53 - 0
atom/browser/api/gpu_info_enumerator.h

@@ -0,0 +1,53 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef ATOM_BROWSER_API_GPU_INFO_ENUMERATOR_H_
+#define ATOM_BROWSER_API_GPU_INFO_ENUMERATOR_H_
+
+#include <memory>
+#include <stack>
+#include <string>
+
+#include "base/values.h"
+#include "gpu/config/gpu_info.h"
+
+namespace atom {
+
+// This class implements the enumerator for reading all the attributes in
+// GPUInfo into a dictionary.
+class GPUInfoEnumerator final : public gpu::GPUInfo::Enumerator {
+  const char* kGPUDeviceKey = "gpuDevice";
+  const char* kVideoDecodeAcceleratorSupportedProfileKey =
+      "videoDecodeAcceleratorSupportedProfile";
+  const char* kVideoEncodeAcceleratorSupportedProfileKey =
+      "videoEncodeAcceleratorSupportedProfile";
+  const char* kAuxAttributesKey = "auxAttributes";
+
+ public:
+  GPUInfoEnumerator();
+  ~GPUInfoEnumerator() override;
+  void AddInt64(const char* name, int64_t value) override;
+  void AddInt(const char* name, int value) override;
+  void AddString(const char* name, const std::string& value) override;
+  void AddBool(const char* name, bool value) override;
+  void AddTimeDeltaInSecondsF(const char* name,
+                              const base::TimeDelta& value) override;
+  void BeginGPUDevice() override;
+  void EndGPUDevice() override;
+  void BeginVideoDecodeAcceleratorSupportedProfile() override;
+  void EndVideoDecodeAcceleratorSupportedProfile() override;
+  void BeginVideoEncodeAcceleratorSupportedProfile() override;
+  void EndVideoEncodeAcceleratorSupportedProfile() override;
+  void BeginAuxAttributes() override;
+  void EndAuxAttributes() override;
+  std::unique_ptr<base::DictionaryValue> GetDictionary();
+
+ private:
+  // The stack is used to manage nested values
+  std::stack<std::unique_ptr<base::DictionaryValue>> value_stack;
+  std::unique_ptr<base::DictionaryValue> current;
+};
+
+}  // namespace atom
+#endif  // ATOM_BROWSER_API_GPU_INFO_ENUMERATOR_H_

+ 93 - 0
atom/browser/api/gpuinfo_manager.cc

@@ -0,0 +1,93 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "atom/browser/api/gpuinfo_manager.h"
+#include "atom/browser/api/gpu_info_enumerator.h"
+#include "base/memory/singleton.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/browser/browser_thread.h"
+#include "gpu/config/gpu_info_collector.h"
+
+namespace atom {
+
+GPUInfoManager* GPUInfoManager::GetInstance() {
+  return base::Singleton<GPUInfoManager>::get();
+}
+
+GPUInfoManager::GPUInfoManager()
+    : gpu_data_manager_(content::GpuDataManagerImpl::GetInstance()) {
+  gpu_data_manager_->AddObserver(this);
+}
+
+GPUInfoManager::~GPUInfoManager() {
+  content::GpuDataManagerImpl::GetInstance()->RemoveObserver(this);
+}
+
+// Based on
+// https://chromium.googlesource.com/chromium/src.git/+/66.0.3359.181/content/browser/gpu/gpu_data_manager_impl_private.cc#810
+bool GPUInfoManager::NeedsCompleteGpuInfoCollection() {
+#if defined(OS_MACOSX)
+  return gpu_data_manager_->GetGPUInfo().gl_vendor.empty();
+#elif defined(OS_WIN)
+  const auto& gpu_info = gpu_data_manager_->GetGPUInfo();
+  return (gpu_info.dx_diagnostics.values.empty() &&
+          gpu_info.dx_diagnostics.children.empty());
+#else
+  return false;
+#endif
+}
+
+// Should be posted to the task runner
+void GPUInfoManager::ProcessCompleteInfo() {
+  const auto result = EnumerateGPUInfo(gpu_data_manager_->GetGPUInfo());
+  // We have received the complete information, resolve all promises that
+  // were waiting for this info.
+  for (const auto& promise : complete_info_promise_set_) {
+    promise->Resolve(*result);
+  }
+  complete_info_promise_set_.clear();
+}
+
+void GPUInfoManager::OnGpuInfoUpdate() {
+  // Ignore if called when not asked for complete GPUInfo
+  if (NeedsCompleteGpuInfoCollection())
+    return;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&GPUInfoManager::ProcessCompleteInfo,
+                                base::Unretained(this)));
+}
+
+// Should be posted to the task runner
+void GPUInfoManager::CompleteInfoFetcher(scoped_refptr<util::Promise> promise) {
+  complete_info_promise_set_.push_back(promise);
+
+  if (NeedsCompleteGpuInfoCollection()) {
+    gpu_data_manager_->RequestCompleteGpuInfoIfNeeded();
+  } else {
+    GPUInfoManager::OnGpuInfoUpdate();
+  }
+}
+
+void GPUInfoManager::FetchCompleteInfo(scoped_refptr<util::Promise> promise) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::BindOnce(&GPUInfoManager::CompleteInfoFetcher,
+                                base::Unretained(this), promise));
+}
+
+// This fetches the info synchronously, so no need to post to the task queue.
+// There cannot be multiple promises as they are resolved synchronously.
+void GPUInfoManager::FetchBasicInfo(scoped_refptr<util::Promise> promise) {
+  gpu::GPUInfo gpu_info;
+  CollectBasicGraphicsInfo(&gpu_info);
+  promise->Resolve(*EnumerateGPUInfo(gpu_info));
+}
+
+std::unique_ptr<base::DictionaryValue> GPUInfoManager::EnumerateGPUInfo(
+    gpu::GPUInfo gpu_info) const {
+  GPUInfoEnumerator enumerator;
+  gpu_info.EnumerateFields(&enumerator);
+  return enumerator.GetDictionary();
+}
+
+}  // namespace atom

+ 49 - 0
atom/browser/api/gpuinfo_manager.h

@@ -0,0 +1,49 @@
+// Copyright (c) 2018 GitHub, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef ATOM_BROWSER_API_GPUINFO_MANAGER_H_
+#define ATOM_BROWSER_API_GPUINFO_MANAGER_H_
+
+#include <memory>
+#include <unordered_set>
+#include <vector>
+
+#include "atom/common/native_mate_converters/value_converter.h"
+#include "atom/common/promise_util.h"
+#include "content/browser/gpu/gpu_data_manager_impl.h"
+#include "content/public/browser/gpu_data_manager.h"
+#include "content/public/browser/gpu_data_manager_observer.h"
+
+namespace atom {
+
+// GPUInfoManager is a singleton used to manage and fetch GPUInfo
+class GPUInfoManager : public content::GpuDataManagerObserver {
+ public:
+  static GPUInfoManager* GetInstance();
+
+  GPUInfoManager();
+  ~GPUInfoManager() override;
+  bool NeedsCompleteGpuInfoCollection();
+  void FetchCompleteInfo(scoped_refptr<util::Promise> promise);
+  void FetchBasicInfo(scoped_refptr<util::Promise> promise);
+  void OnGpuInfoUpdate() override;
+
+ private:
+  std::unique_ptr<base::DictionaryValue> EnumerateGPUInfo(
+      gpu::GPUInfo gpu_info) const;
+
+  // These should be posted to the task queue
+  void CompleteInfoFetcher(scoped_refptr<util::Promise> promise);
+  void ProcessCompleteInfo();
+
+  // This set maintains all the promises that should be fulfilled
+  // once we have the complete information data
+  std::vector<scoped_refptr<util::Promise>> complete_info_promise_set_;
+  content::GpuDataManager* gpu_data_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(GPUInfoManager);
+};
+
+}  // namespace atom
+#endif  // ATOM_BROWSER_API_GPUINFO_MANAGER_H_

+ 1 - 1
atom/common/promise_util.cc

@@ -25,7 +25,7 @@ v8::Maybe<bool> Promise::RejectWithErrorMessage(const std::string& string) {
                             mate::ConvertToV8(isolate(), error));
 }
 
-v8::Local<v8::Object> Promise::GetHandle() const {
+v8::Local<v8::Promise> Promise::GetHandle() const {
   return GetInner()->GetPromise();
 }
 

+ 6 - 5
atom/common/promise_util.h

@@ -14,14 +14,13 @@ namespace atom {
 
 namespace util {
 
-class Promise {
+class Promise : public base::RefCounted<Promise> {
  public:
   explicit Promise(v8::Isolate* isolate);
-  ~Promise();
 
   v8::Isolate* isolate() const { return isolate_; }
 
-  virtual v8::Local<v8::Object> GetHandle() const;
+  virtual v8::Local<v8::Promise> GetHandle() const;
 
   v8::Maybe<bool> Resolve() {
     return GetInner()->Resolve(isolate()->GetCurrentContext(),
@@ -34,13 +33,13 @@ class Promise {
   }
 
   template <typename T>
-  v8::Maybe<bool> Resolve(T* value) {
+  v8::Maybe<bool> Resolve(const T& value) {
     return GetInner()->Resolve(isolate()->GetCurrentContext(),
                                mate::ConvertToV8(isolate(), value));
   }
 
   template <typename T>
-  v8::Maybe<bool> Reject(T* value) {
+  v8::Maybe<bool> Reject(const T& value) {
     return GetInner()->Reject(isolate()->GetCurrentContext(),
                               mate::ConvertToV8(isolate(), value));
   }
@@ -48,6 +47,8 @@ class Promise {
   v8::Maybe<bool> RejectWithErrorMessage(const std::string& error);
 
  protected:
+  virtual ~Promise();
+  friend class base::RefCounted<Promise>;
   v8::Isolate* isolate_;
 
  private:

+ 35 - 0
docs/api/app.md

@@ -905,6 +905,41 @@ Returns [`ProcessMetric[]`](structures/process-metric.md): Array of `ProcessMetr
 
 Returns [`GPUFeatureStatus`](structures/gpu-feature-status.md) - The Graphics Feature Status from `chrome://gpu/`.
 
+### `app.getGPUInfo(infoType)`
+
+* `infoType` String - Values can be either `basic` for basic info or `complete` for complete info.
+
+Returns `Promise`
+
+For `infoType` equal to `complete`:
+ Promise is fulfilled with `Object` containing all the GPU Information as in [chromium's GPUInfo object](https://chromium.googlesource.com/chromium/src.git/+/66.0.3359.181/gpu/config/gpu_info.cc). This includes the version and driver information that's shown on `chrome://gpu` page.
+
+For `infoType` equal to `basic`:
+  Promise is fulfilled with `Object` containing fewer attributes than when requested with `complete`. Here's an example of basic response:
+```js
+{ auxAttributes:
+   { amdSwitchable: true,
+     canSupportThreadedTextureMailbox: false,
+     directComposition: false,
+     directRendering: true,
+     glResetNotificationStrategy: 0,
+     inProcessGpu: true,
+     initializationTime: 0,
+     jpegDecodeAcceleratorSupported: false,
+     optimus: false,
+     passthroughCmdDecoder: false,
+     sandboxed: false,
+     softwareRendering: false,
+     supportsOverlays: false,
+     videoDecodeAcceleratorFlags: 0 },
+gpuDevice:
+   [ { active: true, deviceId: 26657, vendorId: 4098 },
+     { active: false, deviceId: 3366, vendorId: 32902 } ],
+machineModelName: 'MacBookPro',
+machineModelVersion: '11.5' }
+```
+Using `basic` should be preferred if only basic information like `vendorId` or `driverId` is needed.
+
 ### `app.setBadgeCount(count)` _Linux_ _macOS_
 
 * `count` Integer

+ 4 - 0
filenames.gni

@@ -191,6 +191,10 @@ filenames = {
     "atom/browser/api/trackable_object.h",
     "atom/browser/api/frame_subscriber.cc",
     "atom/browser/api/frame_subscriber.h",
+    "atom/browser/api/gpu_info_enumerator.cc",
+    "atom/browser/api/gpu_info_enumerator.h",
+    "atom/browser/api/gpuinfo_manager.cc",
+    "atom/browser/api/gpuinfo_manager.h",
     "atom/browser/api/save_page_handler.cc",
     "atom/browser/api/save_page_handler.h",
     "atom/browser/auto_updater.cc",

+ 55 - 45
native_mate/native_mate/converter.cc

@@ -15,6 +15,7 @@ using v8::Isolate;
 using v8::Local;
 using v8::Number;
 using v8::Object;
+using v8::Promise;
 using v8::String;
 using v8::Value;
 
@@ -33,11 +34,12 @@ bool Converter<bool>::FromV8(Isolate* isolate, Local<Value> val, bool* out) {
 
 #if !defined(OS_LINUX) && !defined(OS_FREEBSD)
 Local<Value> Converter<unsigned long>::ToV8(Isolate* isolate,
-                                             unsigned long val) {
+                                            unsigned long val) {
   return v8::Integer::New(isolate, val);
 }
 
-bool Converter<unsigned long>::FromV8(Isolate* isolate, Local<Value> val,
+bool Converter<unsigned long>::FromV8(Isolate* isolate,
+                                      Local<Value> val,
                                       unsigned long* out) {
   if (!val->IsNumber())
     return false;
@@ -50,7 +52,8 @@ Local<Value> Converter<int32_t>::ToV8(Isolate* isolate, int32_t val) {
   return v8::Integer::New(isolate, val);
 }
 
-bool Converter<int32_t>::FromV8(Isolate* isolate, Local<Value> val,
+bool Converter<int32_t>::FromV8(Isolate* isolate,
+                                Local<Value> val,
                                 int32_t* out) {
   if (!val->IsInt32())
     return false;
@@ -62,7 +65,8 @@ Local<Value> Converter<uint32_t>::ToV8(Isolate* isolate, uint32_t val) {
   return v8::Integer::NewFromUnsigned(isolate, val);
 }
 
-bool Converter<uint32_t>::FromV8(Isolate* isolate, Local<Value> val,
+bool Converter<uint32_t>::FromV8(Isolate* isolate,
+                                 Local<Value> val,
                                  uint32_t* out) {
   if (!val->IsUint32())
     return false;
@@ -74,7 +78,8 @@ Local<Value> Converter<int64_t>::ToV8(Isolate* isolate, int64_t val) {
   return v8::Number::New(isolate, static_cast<double>(val));
 }
 
-bool Converter<int64_t>::FromV8(Isolate* isolate, Local<Value> val,
+bool Converter<int64_t>::FromV8(Isolate* isolate,
+                                Local<Value> val,
                                 int64_t* out) {
   if (!val->IsNumber())
     return false;
@@ -88,7 +93,8 @@ Local<Value> Converter<uint64_t>::ToV8(Isolate* isolate, uint64_t val) {
   return v8::Number::New(isolate, static_cast<double>(val));
 }
 
-bool Converter<uint64_t>::FromV8(Isolate* isolate, Local<Value> val,
+bool Converter<uint64_t>::FromV8(Isolate* isolate,
+                                 Local<Value> val,
                                  uint64_t* out) {
   if (!val->IsNumber())
     return false;
@@ -100,8 +106,7 @@ Local<Value> Converter<float>::ToV8(Isolate* isolate, float val) {
   return v8::Number::New(isolate, val);
 }
 
-bool Converter<float>::FromV8(Isolate* isolate, Local<Value> val,
-                              float* out) {
+bool Converter<float>::FromV8(Isolate* isolate, Local<Value> val, float* out) {
   if (!val->IsNumber())
     return false;
   *out = static_cast<float>(val->NumberValue());
@@ -112,7 +117,8 @@ Local<Value> Converter<double>::ToV8(Isolate* isolate, double val) {
   return v8::Number::New(isolate, val);
 }
 
-bool Converter<double>::FromV8(Isolate* isolate, Local<Value> val,
+bool Converter<double>::FromV8(Isolate* isolate,
+                               Local<Value> val,
                                double* out) {
   if (!val->IsNumber())
     return false;
@@ -120,25 +126,23 @@ bool Converter<double>::FromV8(Isolate* isolate, Local<Value> val,
   return true;
 }
 
-Local<Value> Converter<const char*>::ToV8(
-    Isolate* isolate, const char* val) {
+Local<Value> Converter<const char*>::ToV8(Isolate* isolate, const char* val) {
   return v8::String::NewFromUtf8(isolate, val);
 }
 
-Local<Value> Converter<base::StringPiece>::ToV8(
-    Isolate* isolate, const base::StringPiece& val) {
-  return v8::String::NewFromUtf8(isolate,
-                                 val.data(),
-                                 v8::String::kNormalString,
+Local<Value> Converter<base::StringPiece>::ToV8(Isolate* isolate,
+                                                const base::StringPiece& val) {
+  return v8::String::NewFromUtf8(isolate, val.data(), v8::String::kNormalString,
                                  static_cast<uint32_t>(val.length()));
 }
 
 Local<Value> Converter<std::string>::ToV8(Isolate* isolate,
-                                           const std::string& val) {
+                                          const std::string& val) {
   return Converter<base::StringPiece>::ToV8(isolate, val);
 }
 
-bool Converter<std::string>::FromV8(Isolate* isolate, Local<Value> val,
+bool Converter<std::string>::FromV8(Isolate* isolate,
+                                    Local<Value> val,
                                     std::string* out) {
   if (!val->IsString())
     return false;
@@ -154,83 +158,89 @@ Local<Value> Converter<Local<Function>>::ToV8(Isolate* isolate,
   return val;
 }
 
-bool Converter<Local<Function> >::FromV8(Isolate* isolate, Local<Value> val,
-                                         Local<Function>* out) {
+bool Converter<Local<Function>>::FromV8(Isolate* isolate,
+                                        Local<Value> val,
+                                        Local<Function>* out) {
   if (!val->IsFunction())
     return false;
   *out = Local<Function>::Cast(val);
   return true;
 }
 
-Local<Value> Converter<Local<Object> >::ToV8(Isolate* isolate,
-                                             Local<Object> val) {
+Local<Value> Converter<Local<Object>>::ToV8(Isolate* isolate,
+                                            Local<Object> val) {
   return val;
 }
 
-bool Converter<Local<Object> >::FromV8(Isolate* isolate, Local<Value> val,
-                                       Local<Object>* out) {
+bool Converter<Local<Object>>::FromV8(Isolate* isolate,
+                                      Local<Value> val,
+                                      Local<Object>* out) {
   if (!val->IsObject())
     return false;
   *out = Local<Object>::Cast(val);
   return true;
 }
 
-Local<Value> Converter<Local<String> >::ToV8(Isolate* isolate,
-                                             Local<String> val) {
+Local<Value> Converter<Local<String>>::ToV8(Isolate* isolate,
+                                            Local<String> val) {
   return val;
 }
 
-bool Converter<Local<String> >::FromV8(Isolate* isolate, Local<Value> val,
-                                       Local<String>* out) {
+bool Converter<Local<String>>::FromV8(Isolate* isolate,
+                                      Local<Value> val,
+                                      Local<String>* out) {
   if (!val->IsString())
     return false;
   *out = Local<String>::Cast(val);
   return true;
 }
 
-Local<Value> Converter<Local<External> >::ToV8(Isolate* isolate,
-                                               Local<External> val) {
+Local<Value> Converter<Local<External>>::ToV8(Isolate* isolate,
+                                              Local<External> val) {
   return val;
 }
 
-bool Converter<Local<External> >::FromV8(Isolate* isolate,
-                                          v8::Local<Value> val,
-                                          Local<External>* out) {
+bool Converter<Local<External>>::FromV8(Isolate* isolate,
+                                        v8::Local<Value> val,
+                                        Local<External>* out) {
   if (!val->IsExternal())
     return false;
   *out = Local<External>::Cast(val);
   return true;
 }
 
-Local<Value> Converter<Local<Array> >::ToV8(Isolate* isolate,
-                                            Local<Array> val) {
+Local<Value> Converter<Local<Array>>::ToV8(Isolate* isolate, Local<Array> val) {
   return val;
 }
 
-bool Converter<Local<Array> >::FromV8(Isolate* isolate,
-                                      v8::Local<Value> val,
-                                      Local<Array>* out) {
+bool Converter<Local<Array>>::FromV8(Isolate* isolate,
+                                     v8::Local<Value> val,
+                                     Local<Array>* out) {
   if (!val->IsArray())
     return false;
   *out = Local<Array>::Cast(val);
   return true;
 }
 
-Local<Value> Converter<Local<Value> >::ToV8(Isolate* isolate,
-                                            Local<Value> val) {
+Local<Value> Converter<Local<Value>>::ToV8(Isolate* isolate, Local<Value> val) {
   return val;
 }
 
-bool Converter<Local<Value> >::FromV8(Isolate* isolate, Local<Value> val,
-                                      Local<Value>* out) {
+Local<Promise> Converter<Local<Promise>>::ToV8(Isolate* isolate,
+                                               Local<Promise> val) {
+  return val;
+}
+
+bool Converter<Local<Value>>::FromV8(Isolate* isolate,
+                                     Local<Value> val,
+                                     Local<Value>* out) {
   *out = val;
   return true;
 }
 
 v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate,
-                                      const base::StringPiece& val) {
-  return v8::String::NewFromUtf8(isolate,
-                                 val.data(),
+                                     const base::StringPiece& val) {
+  return v8::String::NewFromUtf8(isolate, val.data(),
                                  v8::String::kInternalizedString,
                                  static_cast<uint32_t>(val.length()));
 }

+ 76 - 76
native_mate/native_mate/converter.h

@@ -6,16 +6,16 @@
 #define NATIVE_MATE_CONVERTER_H_
 
 #include <map>
+#include <set>
 #include <string>
 #include <vector>
-#include <set>
 
 #include "base/strings/string_piece.h"
 #include "v8/include/v8.h"
 
 namespace mate {
 
-template<typename KeyType>
+template <typename KeyType>
 bool SetProperty(v8::Isolate* isolate,
                  v8::Local<v8::Object> object,
                  KeyType key,
@@ -24,188 +24,187 @@ bool SetProperty(v8::Isolate* isolate,
   return !maybe.IsNothing() && maybe.FromJust();
 }
 
-template<typename T>
+template <typename T>
 struct ToV8ReturnsMaybe {
   static const bool value = false;
 };
 
-template<typename T, typename Enable = void>
+template <typename T, typename Enable = void>
 struct Converter {};
 
-template<>
+template <>
 struct Converter<void*> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, void* val) {
     return v8::Undefined(isolate);
   }
 };
 
-template<>
+template <>
 struct Converter<std::nullptr_t> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, std::nullptr_t val) {
     return v8::Null(isolate);
   }
 };
 
-template<>
+template <>
 struct Converter<bool> {
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    bool val);
-  static bool FromV8(v8::Isolate* isolate,
-                     v8::Local<v8::Value> val,
-                     bool* out);
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, bool val);
+  static bool FromV8(v8::Isolate* isolate, v8::Local<v8::Value> val, bool* out);
 };
 
 #if !defined(OS_LINUX) && !defined(OS_FREEBSD)
-template<>
+template <>
 struct Converter<unsigned long> {
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    unsigned long val);
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, unsigned long val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      unsigned long* out);
 };
 #endif
 
-template<>
+template <>
 struct Converter<int32_t> {
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    int32_t val);
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, int32_t val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      int32_t* out);
 };
 
-template<>
+template <>
 struct Converter<uint32_t> {
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    uint32_t val);
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, uint32_t val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      uint32_t* out);
 };
 
-template<>
+template <>
 struct Converter<int64_t> {
   // Warning: JavaScript cannot represent 64 integers precisely.
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    int64_t val);
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, int64_t val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      int64_t* out);
 };
 
-template<>
+template <>
 struct Converter<uint64_t> {
   // Warning: JavaScript cannot represent 64 integers precisely.
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    uint64_t val);
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, uint64_t val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      uint64_t* out);
 };
 
-template<>
+template <>
 struct Converter<float> {
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    float val);
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, float val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      float* out);
 };
 
-template<>
+template <>
 struct Converter<double> {
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    double val);
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, double val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      double* out);
 };
 
-template<>
+template <>
 struct Converter<const char*> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, const char* val);
 };
 
-template<>
+template <>
 struct Converter<base::StringPiece> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    const base::StringPiece& val);
+                                   const base::StringPiece& val);
   // No conversion out is possible because StringPiece does not contain storage.
 };
 
-template<>
+template <>
 struct Converter<std::string> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    const std::string& val);
+                                   const std::string& val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      std::string* out);
 };
 
 v8::Local<v8::String> StringToSymbol(v8::Isolate* isolate,
-                                      const base::StringPiece& input);
+                                     const base::StringPiece& input);
 
 std::string V8ToString(v8::Local<v8::Value> value);
 
-template<>
-struct Converter<v8::Local<v8::Function> > {
+template <>
+struct Converter<v8::Local<v8::Function>> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    v8::Local<v8::Function> val);
+                                   v8::Local<v8::Function> val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      v8::Local<v8::Function>* out);
 };
 
-template<>
-struct Converter<v8::Local<v8::Object> > {
+template <>
+struct Converter<v8::Local<v8::Object>> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    v8::Local<v8::Object> val);
+                                   v8::Local<v8::Object> val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      v8::Local<v8::Object>* out);
 };
 
-template<>
-struct Converter<v8::Local<v8::String> > {
+template <>
+struct Converter<v8::Local<v8::String>> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    v8::Local<v8::String> val);
+                                   v8::Local<v8::String> val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      v8::Local<v8::String>* out);
 };
 
-template<>
-struct Converter<v8::Local<v8::External> > {
+template <>
+struct Converter<v8::Local<v8::External>> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    v8::Local<v8::External> val);
+                                   v8::Local<v8::External> val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      v8::Local<v8::External>* out);
 };
 
-template<>
-struct Converter<v8::Local<v8::Array> > {
+template <>
+struct Converter<v8::Local<v8::Array>> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    v8::Local<v8::Array> val);
+                                   v8::Local<v8::Array> val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      v8::Local<v8::Array>* out);
 };
 
-template<>
-struct Converter<v8::Local<v8::Value> > {
+template <>
+struct Converter<v8::Local<v8::Value>> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    v8::Local<v8::Value> val);
+                                   v8::Local<v8::Value> val);
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
                      v8::Local<v8::Value>* out);
 };
 
-template<typename T>
-struct Converter<std::vector<T> > {
+template <>
+struct Converter<v8::Local<v8::Promise>> {
+  static v8::Local<v8::Promise> ToV8(v8::Isolate* isolate,
+                                     v8::Local<v8::Promise> val);
+  // static bool FromV8(v8::Isolate* isolate,
+  //                    v8::Local<v8::Value> val,
+  //                    v8::Local<v8::Value>* out);
+};
+
+template <typename T>
+struct Converter<std::vector<T>> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    const std::vector<T>& val) {
+                                   const std::vector<T>& val) {
     v8::Local<v8::Array> result(
         v8::Array::New(isolate, static_cast<int>(val.size())));
     for (size_t i = 0; i < val.size(); ++i) {
@@ -235,10 +234,10 @@ struct Converter<std::vector<T> > {
   }
 };
 
-template<typename T>
-struct Converter<std::set<T> > {
+template <typename T>
+struct Converter<std::set<T>> {
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                    const std::set<T>& val) {
+                                   const std::set<T>& val) {
     v8::Local<v8::Array> result(
         v8::Array::New(isolate, static_cast<int>(val.size())));
     typename std::set<T>::const_iterator it;
@@ -269,11 +268,11 @@ struct Converter<std::set<T> > {
   }
 };
 
-template<typename T>
-struct Converter<std::map<std::string, T> > {
+template <typename T>
+struct Converter<std::map<std::string, T>> {
   static bool FromV8(v8::Isolate* isolate,
                      v8::Local<v8::Value> val,
-                     std::map<std::string, T> * out) {
+                     std::map<std::string, T>* out) {
     if (!val->IsObject())
       return false;
 
@@ -288,18 +287,18 @@ struct Converter<std::map<std::string, T> > {
     return true;
   }
   static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                   const std::map<std::string, T>& val) {
+                                   const std::map<std::string, T>& val) {
     v8::Local<v8::Object> result = v8::Object::New(isolate);
     for (auto i = val.begin(); i != val.end(); i++) {
       result->Set(Converter<T>::ToV8(isolate, i->first),
-                 Converter<T>::ToV8(isolate, i->second));
+                  Converter<T>::ToV8(isolate, i->second));
     }
     return result;
   }
 };
 
 // Convenience functions that deduce T.
-template<typename T>
+template <typename T>
 v8::Local<v8::Value> ConvertToV8(v8::Isolate* isolate, const T& input) {
   return Converter<T>::ToV8(isolate, input);
 }
@@ -309,13 +308,14 @@ inline v8::Local<v8::Value> ConvertToV8(v8::Isolate* isolate,
   return Converter<const char*>::ToV8(isolate, input);
 }
 
-template<typename T>
+template <typename T>
 v8::MaybeLocal<v8::Value> ConvertToV8(v8::Local<v8::Context> context,
                                       const T& input) {
   return Converter<T>::ToV8(context, input);
 }
 
-template<typename T, bool = ToV8ReturnsMaybe<T>::value> struct ToV8Traits;
+template <typename T, bool = ToV8ReturnsMaybe<T>::value>
+struct ToV8Traits;
 
 template <typename T>
 struct ToV8Traits<T, true> {
@@ -347,15 +347,15 @@ bool TryConvertToV8(v8::Isolate* isolate,
   return ToV8Traits<T>::TryConvertToV8(isolate, input, output);
 }
 
-template<typename T>
-bool ConvertFromV8(v8::Isolate* isolate, v8::Local<v8::Value> input,
+template <typename T>
+bool ConvertFromV8(v8::Isolate* isolate,
+                   v8::Local<v8::Value> input,
                    T* result) {
   return Converter<T>::FromV8(isolate, input, result);
 }
 
-inline v8::Local<v8::String> StringToV8(
-    v8::Isolate* isolate,
-    const base::StringPiece& input) {
+inline v8::Local<v8::String> StringToV8(v8::Isolate* isolate,
+                                        const base::StringPiece& input) {
   return ConvertToV8(isolate, input).As<v8::String>();
 }
 

+ 31 - 0
spec/api-app-spec.js

@@ -804,6 +804,37 @@ describe('app module', () => {
     })
   })
 
+  describe('getGPUInfo() API', () => {
+    it('succeeds with basic GPUInfo', (done) => {
+      app.getGPUInfo('basic').then((gpuInfo) => {
+        // Devices information is always present in the available info
+        expect(gpuInfo.gpuDevice).to.be.an('array')
+        expect(gpuInfo.gpuDevice.length).to.be.greaterThan(0)
+        const device = gpuInfo.gpuDevice[0]
+        expect(device).to.be.an('object')
+        expect(device)
+          .to.have.property('deviceId')
+          .that.is.a('number')
+          .not.lessThan(0)
+        done()
+      })
+    })
+
+    it('succeeds with complete GPUInfo', (done) => {
+      app.getGPUInfo('complete').then((completeInfo) => {
+        // Driver version is present in the complete info
+        expect(completeInfo.auxAttributes.glVersion).to.be.a('string').that.has.length.greaterThan(0)
+        done()
+      })
+    })
+
+    it('fails for invalid info_type', (done) => {
+      app.getGPUInfo('invalid').catch(() => {
+        done()
+      })
+    })
+  })
+
   describe('mixed sandbox option', () => {
     let appProcess = null
     let server = null