// 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 "atom/browser/browser.h" #include #include #include #include #include #include "base/base_paths.h" #include "base/file_version_info.h" #include "base/files/file_path.h" #include "base/path_service.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/win/win_util.h" #include "base/win/registry.h" #include "base/win/windows_version.h" #include "atom/common/atom_version.h" namespace atom { namespace { const wchar_t kAppUserModelIDFormat[] = L"electron.app.$1"; BOOL CALLBACK WindowsEnumerationHandler(HWND hwnd, LPARAM param) { DWORD target_process_id = *reinterpret_cast(param); DWORD process_id = 0; GetWindowThreadProcessId(hwnd, &process_id); if (process_id == target_process_id) { SetFocus(hwnd); return FALSE; } return TRUE; } } // namespace void Browser::Focus() { // On Windows we just focus on the first window found for this process. DWORD pid = GetCurrentProcessId(); EnumWindows(&WindowsEnumerationHandler, reinterpret_cast(&pid)); } void Browser::AddRecentDocument(const base::FilePath& path) { if (base::win::GetVersion() < base::win::VERSION_WIN7) return; CComPtr item; HRESULT hr = SHCreateItemFromParsingName( path.value().c_str(), NULL, IID_PPV_ARGS(&item)); if (SUCCEEDED(hr)) { SHARDAPPIDINFO info; info.psi = item; info.pszAppID = GetAppUserModelID(); SHAddToRecentDocs(SHARD_APPIDINFO, &info); } } void Browser::ClearRecentDocuments() { CComPtr destinations; if (FAILED(destinations.CoCreateInstance(CLSID_ApplicationDestinations, NULL, CLSCTX_INPROC_SERVER))) return; if (FAILED(destinations->SetAppID(GetAppUserModelID()))) return; destinations->RemoveAllDestinations(); } void Browser::SetAppUserModelID(const base::string16& name) { app_user_model_id_ = name; SetCurrentProcessExplicitAppUserModelID(app_user_model_id_.c_str()); } bool Browser::SetUserTasks(const std::vector& tasks) { CComPtr destinations; if (FAILED(destinations.CoCreateInstance(CLSID_DestinationList))) return false; if (FAILED(destinations->SetAppID(GetAppUserModelID()))) return false; // Start a transaction that updates the JumpList of this application. UINT max_slots; CComPtr removed; if (FAILED(destinations->BeginList(&max_slots, IID_PPV_ARGS(&removed)))) return false; CComPtr collection; if (FAILED(collection.CoCreateInstance(CLSID_EnumerableObjectCollection))) return false; for (auto& task : tasks) { CComPtr link; if (FAILED(link.CoCreateInstance(CLSID_ShellLink)) || FAILED(link->SetPath(task.program.value().c_str())) || FAILED(link->SetArguments(task.arguments.c_str())) || FAILED(link->SetDescription(task.description.c_str()))) return false; if (!task.icon_path.empty() && FAILED(link->SetIconLocation(task.icon_path.value().c_str(), task.icon_index))) return false; CComQIPtr property_store = link; if (!base::win::SetStringValueForPropertyStore(property_store, PKEY_Title, task.title.c_str())) return false; if (FAILED(collection->AddObject(link))) return false; } // When the list is empty "AddUserTasks" could fail, so we don't check return // value for it. CComQIPtr task_array = collection; destinations->AddUserTasks(task_array); return SUCCEEDED(destinations->CommitList()); } std::wstring protocolLaunchPath(std::string protocol, mate::Arguments* args) { // Read in optional exe path arg std::wstring exePath; std::string rawExePath; base::FilePath path; if (!args->GetNext(&rawExePath)) { if (!PathService::Get(base::FILE_EXE, &path)) { LOG(ERROR) << "Error getting app exe path"; return L""; } // Executable Path exePath = path.value(); } else { exePath.assign(rawExePath.begin(), rawExePath.end()); } // Read in optional args arg std::vector launchArgs; args->GetNext(&launchArgs); std::wstring exe = L"\"" + exePath + L"\" "; // Parse launch args into a string of space spearated args if (launchArgs.size() != 0) { std::string launchArgString = ""; for (std::string launchArg : launchArgs) { launchArgString = launchArgString + launchArg + " "; } std::wstring wLaunchArgString; wLaunchArgString.assign(launchArgString.begin(), launchArgString.end()); exe = exe + L"\"" + wLaunchArgString + L"\""; } return exe + L"\"%1\""; } bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol, mate::Arguments* args) { if (protocol.empty()) return false; // Main Registry Key HKEY root = HKEY_CURRENT_USER; std::string keyPathStr = "Software\\Classes\\" + protocol; std::wstring keyPath = std::wstring(keyPathStr.begin(), keyPathStr.end()); // Command Key std::string cmdPathStr = keyPathStr + "\\shell\\open\\command"; std::wstring cmdPath = std::wstring(cmdPathStr.begin(), cmdPathStr.end()); base::win::RegKey key; base::win::RegKey commandKey; if (FAILED(key.Open(root, keyPath.c_str(), KEY_ALL_ACCESS))) // Key doesn't even exist, we can confirm that it is not set return true; if (FAILED(commandKey.Open(root, cmdPath.c_str(), KEY_ALL_ACCESS))) // Key doesn't even exist, we can confirm that it is not set return true; std::wstring keyVal; if (FAILED(commandKey.ReadValue(L"", &keyVal))) // Default value not set, we can confirm that it is not set return true; std::wstring exe = protocolLaunchPath(protocol, args); if (exe == L"") return false; if (keyVal == exe) { // Let's kill the key if (FAILED(key.DeleteKey(L"shell"))) return false; return true; } else { return true; } } bool Browser::SetAsDefaultProtocolClient(const std::string& protocol, mate::Arguments* args) { // HKEY_CLASSES_ROOT // $PROTOCOL // (Default) = "URL:$NAME" // URL Protocol = "" // shell // open // command // (Default) = "$COMMAND" "%1" // // However, the "HKEY_CLASSES_ROOT" key can only be written by the // Administrator user. So, we instead write to "HKEY_CURRENT_USER\ // Software\Classes", which is inherited by "HKEY_CLASSES_ROOT" // anyway, and can be written by unprivileged users. if (protocol.empty()) return false; std::wstring exe = protocolLaunchPath(protocol, args); if (exe == L"") { return false; } // Main Registry Key HKEY root = HKEY_CURRENT_USER; std::string keyPathStr = "Software\\Classes\\" + protocol; std::wstring keyPath = std::wstring(keyPathStr.begin(), keyPathStr.end()); std::string urlDeclStr = "URL:" + protocol; std::wstring urlDecl = std::wstring(urlDeclStr.begin(), urlDeclStr.end()); // Command Key std::string cmdPathStr = keyPathStr + "\\shell\\open\\command"; std::wstring cmdPath = std::wstring(cmdPathStr.begin(), cmdPathStr.end()); // Write information to registry base::win::RegKey key(root, keyPath.c_str(), KEY_ALL_ACCESS); if (FAILED(key.WriteValue(L"URL Protocol", L"")) || FAILED(key.WriteValue(L"", urlDecl.c_str()))) return false; base::win::RegKey commandKey(root, cmdPath.c_str(), KEY_ALL_ACCESS); if (FAILED(commandKey.WriteValue(L"", exe.c_str()))) return false; return true; } bool Browser::IsDefaultProtocolClient(const std::string& protocol, mate::Arguments* args) { if (protocol.empty()) return false; std::wstring exe = protocolLaunchPath(protocol, args); if (exe == L"") return false; // Main Registry Key HKEY root = HKEY_CURRENT_USER; std::string keyPathStr = "Software\\Classes\\" + protocol; std::wstring keyPath = std::wstring(keyPathStr.begin(), keyPathStr.end()); // Command Key std::string cmdPathStr = keyPathStr + "\\shell\\open\\command"; std::wstring cmdPath = std::wstring(cmdPathStr.begin(), cmdPathStr.end()); base::win::RegKey key; base::win::RegKey commandKey; if (FAILED(key.Open(root, keyPath.c_str(), KEY_ALL_ACCESS))) // Key doesn't exist, we can confirm that it is not set return false; if (FAILED(commandKey.Open(root, cmdPath.c_str(), KEY_ALL_ACCESS))) // Key doesn't exist, we can confirm that it is not set return false; std::wstring keyVal; if (FAILED(commandKey.ReadValue(L"", &keyVal))) // Default value not set, we can confirm that it is not set return false; if (keyVal == exe) { // Default value is the same as current file path return true; } else { return false; } } bool Browser::SetBadgeCount(int count) { return false; } void Browser::SetLoginItemSettings(LoginItemSettings settings) { std::wstring keyPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"; base::win::RegKey key(HKEY_CURRENT_USER, keyPath.c_str(), KEY_ALL_ACCESS); if (settings.open_at_login) { base::FilePath path; if (PathService::Get(base::FILE_EXE, &path)) { std::wstring exePath(path.value()); key.WriteValue(GetAppUserModelID(), exePath.c_str()); } } else { key.DeleteValue(GetAppUserModelID()); } } Browser::LoginItemSettings Browser::GetLoginItemSettings() { LoginItemSettings settings; std::wstring keyPath = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"; base::win::RegKey key(HKEY_CURRENT_USER, keyPath.c_str(), KEY_ALL_ACCESS); std::wstring keyVal; if (!FAILED(key.ReadValue(GetAppUserModelID(), &keyVal))) { base::FilePath path; if (PathService::Get(base::FILE_EXE, &path)) { std::wstring exePath(path.value()); settings.open_at_login = keyVal == exePath; } } return settings; } PCWSTR Browser::GetAppUserModelID() { if (app_user_model_id_.empty()) { SetAppUserModelID(base::ReplaceStringPlaceholders( kAppUserModelIDFormat, base::UTF8ToUTF16(GetName()), nullptr)); } return app_user_model_id_.c_str(); } std::string Browser::GetExecutableFileVersion() const { base::FilePath path; if (PathService::Get(base::FILE_EXE, &path)) { std::unique_ptr version_info( FileVersionInfo::CreateFileVersionInfo(path)); return base::UTF16ToUTF8(version_info->product_version()); } return ATOM_VERSION_STRING; } std::string Browser::GetExecutableFileProductName() const { base::FilePath path; if (PathService::Get(base::FILE_EXE, &path)) { std::unique_ptr version_info( FileVersionInfo::CreateFileVersionInfo(path)); return base::UTF16ToUTF8(version_info->product_name()); } return ATOM_PRODUCT_NAME; } } // namespace atom