2121#include " base/win/registry.h"
2222#include " base/win/win_util.h"
2323#include " base/win/windows_version.h"
24+ #include " chrome/browser/icon_manager.h"
2425#include " electron/electron_version.h"
26+ #include " shell/browser/api/electron_api_app.h"
27+ #include " shell/browser/electron_browser_main_parts.h"
2528#include " shell/browser/ui/message_box.h"
2629#include " shell/browser/ui/win/jump_list.h"
2730#include " shell/common/application_info.h"
31+ #include " shell/common/gin_converters/file_path_converter.h"
32+ #include " shell/common/gin_converters/image_converter.h"
2833#include " shell/common/gin_helper/arguments.h"
34+ #include " shell/common/gin_helper/dictionary.h"
2935#include " shell/common/skia_util.h"
3036#include " ui/events/keycodes/keyboard_code_conversion_win.h"
3137
@@ -49,7 +55,6 @@ BOOL CALLBACK WindowsEnumerationHandler(HWND hwnd, LPARAM param) {
4955bool GetProcessExecPath (base::string16* exe) {
5056 base::FilePath path;
5157 if (!base::PathService::Get (base::FILE_EXE, &path)) {
52- LOG (ERROR) << " Error getting app exe path" ;
5358 return false ;
5459 }
5560 *exe = path.value ();
@@ -81,30 +86,58 @@ bool IsValidCustomProtocol(const base::string16& scheme) {
8186 return cmd_key.Valid () && cmd_key.HasValue (L" URL Protocol" );
8287}
8388
89+ // Helper for GetApplicationInfoForProtocol().
90+ // takes in an assoc_str
91+ // (https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/ne-shlwapi-assocstr)
92+ // and returns the application name, icon and path that handles the protocol.
93+ //
8494// Windows 8 introduced a new protocol->executable binding system which cannot
8595// be retrieved in the HKCR registry subkey method implemented below. We call
8696// AssocQueryString with the new Win8-only flag ASSOCF_IS_PROTOCOL instead.
87- base::string16 GetAppForProtocolUsingAssocQuery (const GURL& url) {
97+ base::string16 GetAppInfoHelperForProtocol (ASSOCSTR assoc_str,
98+ const GURL& url) {
8899 const base::string16 url_scheme = base::ASCIIToUTF16 (url.scheme ());
89100 if (!IsValidCustomProtocol (url_scheme))
90101 return base::string16 ();
91102
92- // Query AssocQueryString for a human-readable description of the program
93- // that will be invoked given the provided URL spec. This is used only to
94- // populate the external protocol dialog box the user sees when invoking
95- // an unknown external protocol.
96103 wchar_t out_buffer[1024 ];
97104 DWORD buffer_size = base::size (out_buffer);
98105 HRESULT hr =
99- AssocQueryString (ASSOCF_IS_PROTOCOL, ASSOCSTR_FRIENDLYAPPNAME ,
100- url_scheme. c_str (), NULL , out_buffer, &buffer_size);
106+ AssocQueryString (ASSOCF_IS_PROTOCOL, assoc_str, url_scheme. c_str (), NULL ,
107+ out_buffer, &buffer_size);
101108 if (FAILED (hr)) {
102109 DLOG (WARNING) << " AssocQueryString failed!" ;
103110 return base::string16 ();
104111 }
105112 return base::string16 (out_buffer);
106113}
107114
115+ void OnIconDataAvailable (const base::FilePath& app_path,
116+ const base::string16& app_display_name,
117+ gin_helper::Promise<gin_helper::Dictionary> promise,
118+ gfx::Image icon) {
119+ if (!icon.IsEmpty ()) {
120+ v8::HandleScope scope (promise.isolate ());
121+ gin_helper::Dictionary dict =
122+ gin::Dictionary::CreateEmpty (promise.isolate ());
123+
124+ dict.Set (" path" , app_path);
125+ dict.Set (" name" , app_display_name);
126+ dict.Set (" icon" , icon);
127+ promise.Resolve (dict);
128+ } else {
129+ promise.RejectWithErrorMessage (" Failed to get file icon." );
130+ }
131+ }
132+
133+ base::string16 GetAppDisplayNameForProtocol (const GURL& url) {
134+ return GetAppInfoHelperForProtocol (ASSOCSTR_FRIENDLYAPPNAME, url);
135+ }
136+
137+ base::string16 GetAppPathForProtocol (const GURL& url) {
138+ return GetAppInfoHelperForProtocol (ASSOCSTR_EXECUTABLE, url);
139+ }
140+
108141base::string16 GetAppForProtocolUsingRegistry (const GURL& url) {
109142 const base::string16 url_scheme = base::ASCIIToUTF16 (url.scheme ());
110143 if (!IsValidCustomProtocol (url_scheme))
@@ -169,6 +202,96 @@ void Browser::Focus(gin_helper::Arguments* args) {
169202 EnumWindows (&WindowsEnumerationHandler, reinterpret_cast <LPARAM>(&pid));
170203}
171204
205+ void GetFileIcon (const base::FilePath& path,
206+ v8::Isolate* isolate,
207+ base::CancelableTaskTracker* cancelable_task_tracker_,
208+ const base::string16 app_display_name,
209+ gin_helper::Promise<gin_helper::Dictionary> promise) {
210+ base::FilePath normalized_path = path.NormalizePathSeparators ();
211+ IconLoader::IconSize icon_size = IconLoader::IconSize::LARGE;
212+
213+ auto * icon_manager = ElectronBrowserMainParts::Get ()->GetIconManager ();
214+ gfx::Image* icon =
215+ icon_manager->LookupIconFromFilepath (normalized_path, icon_size);
216+ if (icon) {
217+ gin_helper::Dictionary dict = gin::Dictionary::CreateEmpty (isolate);
218+ dict.Set (" icon" , *icon);
219+ dict.Set (" name" , app_display_name);
220+ dict.Set (" path" , normalized_path);
221+ promise.Resolve (dict);
222+ } else {
223+ icon_manager->LoadIcon (normalized_path, icon_size,
224+ base::BindOnce (&OnIconDataAvailable, normalized_path,
225+ app_display_name, std::move (promise)),
226+ cancelable_task_tracker_);
227+ }
228+ }
229+
230+ void GetApplicationInfoForProtocolUsingRegistry (
231+ v8::Isolate* isolate,
232+ const GURL& url,
233+ gin_helper::Promise<gin_helper::Dictionary> promise,
234+ base::CancelableTaskTracker* cancelable_task_tracker_) {
235+ base::FilePath app_path;
236+
237+ const base::string16 url_scheme = base::ASCIIToUTF16 (url.scheme ());
238+ if (!IsValidCustomProtocol (url_scheme)) {
239+ promise.RejectWithErrorMessage (" invalid url_scheme" );
240+ return ;
241+ }
242+ base::string16 command_to_launch;
243+ const base::string16 cmd_key_path = url_scheme + L" \\ shell\\ open\\ command" ;
244+ base::win::RegKey cmd_key_exe (HKEY_CLASSES_ROOT, cmd_key_path.c_str (),
245+ KEY_READ);
246+ if (cmd_key_exe.ReadValue (NULL , &command_to_launch) == ERROR_SUCCESS) {
247+ base::CommandLine command_line (
248+ base::CommandLine::FromString (command_to_launch));
249+ app_path = command_line.GetProgram ();
250+ } else {
251+ promise.RejectWithErrorMessage (
252+ " Unable to retrieve installation path to app" );
253+ return ;
254+ }
255+ const base::string16 app_display_name = GetAppForProtocolUsingRegistry (url);
256+
257+ if (app_display_name.length () == 0 ) {
258+ promise.RejectWithErrorMessage (
259+ " Unable to retrieve application display name" );
260+ return ;
261+ }
262+ GetFileIcon (app_path, isolate, cancelable_task_tracker_, app_display_name,
263+ std::move (promise));
264+ }
265+
266+ // resolves `Promise<Object>` - Resolve with an object containing the following:
267+ // * `icon` NativeImage - the display icon of the app handling the protocol.
268+ // * `path` String - installation path of the app handling the protocol.
269+ // * `name` String - display name of the app handling the protocol.
270+ void GetApplicationInfoForProtocolUsingAssocQuery (
271+ v8::Isolate* isolate,
272+ const GURL& url,
273+ gin_helper::Promise<gin_helper::Dictionary> promise,
274+ base::CancelableTaskTracker* cancelable_task_tracker_) {
275+ base::string16 app_path = GetAppPathForProtocol (url);
276+
277+ if (app_path.empty ()) {
278+ promise.RejectWithErrorMessage (
279+ " Unable to retrieve installation path to app" );
280+ return ;
281+ }
282+
283+ base::string16 app_display_name = GetAppDisplayNameForProtocol (url);
284+
285+ if (app_display_name.empty ()) {
286+ promise.RejectWithErrorMessage (" Unable to retrieve display name of app" );
287+ return ;
288+ }
289+
290+ base::FilePath app_path_file_path = base::FilePath (app_path);
291+ GetFileIcon (app_path_file_path, isolate, cancelable_task_tracker_,
292+ app_display_name, std::move (promise));
293+ }
294+
172295void Browser::AddRecentDocument (const base::FilePath& path) {
173296 CComPtr<IShellItem> item;
174297 HRESULT hr = SHCreateItemFromParsingName (path.value ().c_str (), NULL ,
@@ -358,14 +481,32 @@ bool Browser::IsDefaultProtocolClient(const std::string& protocol,
358481base::string16 Browser::GetApplicationNameForProtocol (const GURL& url) {
359482 // Windows 8 or above has a new protocol association query.
360483 if (base::win::GetVersion () >= base::win::Version::WIN8) {
361- base::string16 application_name = GetAppForProtocolUsingAssocQuery (url);
484+ base::string16 application_name = GetAppDisplayNameForProtocol (url);
362485 if (!application_name.empty ())
363486 return application_name;
364487 }
365488
366489 return GetAppForProtocolUsingRegistry (url);
367490}
368491
492+ v8::Local<v8::Promise> Browser::GetApplicationInfoForProtocol (
493+ v8::Isolate* isolate,
494+ const GURL& url) {
495+ gin_helper::Promise<gin_helper::Dictionary> promise (isolate);
496+ v8::Local<v8::Promise> handle = promise.GetHandle ();
497+
498+ // Windows 8 or above has a new protocol association query.
499+ if (base::win::GetVersion () >= base::win::Version::WIN8) {
500+ GetApplicationInfoForProtocolUsingAssocQuery (
501+ isolate, url, std::move (promise), &cancelable_task_tracker_);
502+ return handle;
503+ }
504+
505+ GetApplicationInfoForProtocolUsingRegistry (isolate, url, std::move (promise),
506+ &cancelable_task_tracker_);
507+ return handle;
508+ }
509+
369510bool Browser::SetBadgeCount (int count) {
370511 return false ;
371512}
0 commit comments