Skip to content

Commit 3183eca

Browse files
committed
Pin to taskbar on Windows
fix brave/brave-browser#24054 * Pin to taskbar from first run or default browser dialog when user want to do it. * Pin to taskbar from settings. Use upstream's api for pin state checking pin to taskbar With this, this pin to shortcut feature is available also on Win7/8.
1 parent c6023bb commit 3183eca

28 files changed

+565
-353
lines changed

app/brave_generated_resources.grd

+5
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,11 @@ Or change later at <ph name="SETTINGS_EXTENIONS_LINK">$2<ex>brave://settings/ext
928928
Maybe later
929929
</message>
930930

931+
<if expr="is_win">
932+
<message name="IDS_FIRSTRUN_DLG_PIN_SHORTCUT_TEXT" desc="Text for pin to taskbar checkbox">
933+
Pin to taskbar
934+
</message>
935+
</if>
931936
<!-- Importer -->
932937
<message name="IDS_BRAVE_IMPORT_FROM_EDGE" desc="browser combo box: Microsoft Edge Legacy">
933938
Microsoft Edge Legacy

app/brave_settings_strings.grdp

+13
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,19 @@
140140
Customize the background image and widgets that appear on the new tab page
141141
</message>
142142

143+
<!-- Settings / Pin shortcut-->
144+
<if expr="is_win">
145+
<message name="IDS_SETTINGS_CAN_PIN_SHORTCUT">
146+
Pin to taskbar
147+
</message>
148+
<message name="IDS_SETTINGS_PIN_SHORTCUT">
149+
Pin
150+
</message>
151+
<message name="IDS_SETTINGS_SHORTCUT_PINNED">
152+
Brave is already pinned
153+
</message>
154+
</if>
155+
143156
<!-- Settings / Shields -->
144157
<message name="IDS_SETTINGS_BRAVE_SHIELDS_TITLE" desc="The title for Brave shields section in settings">
145158
Shields

browser/brave_shell_integration_win.cc

+54-206
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55

66
#include "brave/browser/brave_shell_integration_win.h"
77

8-
#include <shlobj.h>
9-
#include <wrl/client.h>
10-
118
#include <memory>
129
#include <string>
1310
#include <tuple>
@@ -21,7 +18,6 @@
2118
#include "base/strings/string_util.h"
2219
#include "base/task/thread_pool.h"
2320
#include "base/win/shortcut.h"
24-
#include "base/win/windows_version.h"
2521
#include "chrome/browser/browser_process.h"
2622
#include "chrome/browser/profiles/profile.h"
2723
#include "chrome/browser/profiles/profile_attributes_entry.h"
@@ -30,6 +26,7 @@
3026
#include "chrome/browser/shell_integration_win.h"
3127
#include "chrome/installer/util/install_util.h"
3228
#include "chrome/installer/util/shell_util.h"
29+
#include "chrome/installer/util/taskbar_util.h"
3330
#include "content/public/browser/browser_thread.h"
3431
#include "third_party/abseil-cpp/absl/types/optional.h"
3532

@@ -106,120 +103,13 @@ absl::optional<ScopedShortcutFile> GetShortcutPath(
106103
return absl::optional<ScopedShortcutFile>(shortcut_path);
107104
}
108105

109-
// NOTE: Below Pin/IsPin method is copied lastest chromium.
110-
// Delete and use upstreams one when it's available from our trunk.
111-
112-
// ScopedPIDLFromPath class, and the idea of using IPinnedList3::Modify,
113-
// are thanks to Gee Law <https://geelaw.blog/entries/msedge-pins/>
114-
class ScopedPIDLFromPath {
115-
public:
116-
explicit ScopedPIDLFromPath(PCWSTR path)
117-
: p_id_list_(ILCreateFromPath(path)) {}
118-
~ScopedPIDLFromPath() {
119-
if (p_id_list_)
120-
ILFree(p_id_list_);
121-
}
122-
PIDLIST_ABSOLUTE Get() const { return p_id_list_; }
123-
124-
private:
125-
PIDLIST_ABSOLUTE const p_id_list_;
126-
};
127-
128-
enum class PinnedListModifyCaller { kExplorer = 4 };
129-
130-
constexpr GUID CLSID_TaskbandPin = {
131-
0x90aa3a4e,
132-
0x1cba,
133-
0x4233,
134-
{0xb8, 0xbb, 0x53, 0x57, 0x73, 0xd4, 0x84, 0x49}};
135-
136-
// Undocumented COM interface for manipulating taskbar pinned list.
137-
class __declspec(uuid("0DD79AE2-D156-45D4-9EEB-3B549769E940")) IPinnedList3
138-
: public IUnknown {
139-
public:
140-
virtual HRESULT STDMETHODCALLTYPE EnumObjects() = 0;
141-
virtual HRESULT STDMETHODCALLTYPE GetPinnableInfo() = 0;
142-
virtual HRESULT STDMETHODCALLTYPE IsPinnable() = 0;
143-
virtual HRESULT STDMETHODCALLTYPE Resolve() = 0;
144-
virtual HRESULT STDMETHODCALLTYPE LegacyModify() = 0;
145-
virtual HRESULT STDMETHODCALLTYPE GetChangeCount() = 0;
146-
virtual HRESULT STDMETHODCALLTYPE IsPinned(PCIDLIST_ABSOLUTE) = 0;
147-
virtual HRESULT STDMETHODCALLTYPE GetPinnedItem() = 0;
148-
virtual HRESULT STDMETHODCALLTYPE GetAppIDForPinnedItem() = 0;
149-
virtual HRESULT STDMETHODCALLTYPE ItemChangeNotify() = 0;
150-
virtual HRESULT STDMETHODCALLTYPE UpdateForRemovedItemsAsNecessary() = 0;
151-
virtual HRESULT STDMETHODCALLTYPE PinShellLink() = 0;
152-
virtual HRESULT STDMETHODCALLTYPE GetPinnedItemForAppID() = 0;
153-
virtual HRESULT STDMETHODCALLTYPE Modify(PCIDLIST_ABSOLUTE unpin,
154-
PCIDLIST_ABSOLUTE pin,
155-
PinnedListModifyCaller caller) = 0;
156-
};
157-
158-
// Returns the taskbar pinned list if successful, an empty ComPtr otherwise.
159-
Microsoft::WRL::ComPtr<IPinnedList3> GetTaskbarPinnedList() {
160-
if (base::win::GetVersion() < base::win::Version::WIN10_RS5)
161-
return nullptr;
162-
163-
Microsoft::WRL::ComPtr<IPinnedList3> pinned_list;
164-
if (FAILED(CoCreateInstance(CLSID_TaskbandPin, nullptr, CLSCTX_INPROC_SERVER,
165-
IID_PPV_ARGS(&pinned_list)))) {
166-
return nullptr;
167-
}
168-
169-
return pinned_list;
170-
}
171-
172-
void PinShortcutWin10(const base::FilePath& shortcut) {
173-
Microsoft::WRL::ComPtr<IPinnedList3> pinned_list = GetTaskbarPinnedList();
174-
if (!pinned_list)
175-
return;
176-
177-
ScopedPIDLFromPath item_id_list(shortcut.value().data());
178-
pinned_list->Modify(nullptr, item_id_list.Get(),
179-
PinnedListModifyCaller::kExplorer);
180-
}
181-
182-
absl::optional<bool> IsShortcutPinnedWin10(const base::FilePath& shortcut) {
183-
Microsoft::WRL::ComPtr<IPinnedList3> pinned_list = GetTaskbarPinnedList();
184-
if (!pinned_list.Get())
185-
return absl::nullopt;
186-
187-
ScopedPIDLFromPath item_id_list(shortcut.value().data());
188-
HRESULT hr = pinned_list->IsPinned(item_id_list.Get());
189-
// S_OK means `shortcut` is pinned, S_FALSE mean it's not pinned.
190-
return SUCCEEDED(hr) ? absl::optional<bool>(hr == S_OK) : absl::nullopt;
191-
}
192-
193-
bool IsShortcutPinned(const ShellUtil::ShortcutProperties& properties) {
194-
// Generate the shortcut to check pin state.
195-
absl::optional<ScopedShortcutFile> shortcut_path =
196-
GetShortcutPath(ExtractShortcutNameFromProperties(properties));
197-
if (!shortcut_path) {
198-
LOG(ERROR) << __func__ << " failed to get shortcut path";
199-
return false;
200-
}
201-
202-
if (!CreateShortcut(properties, shortcut_path->file_path())) {
203-
LOG(ERROR) << __func__ << " Failed to create shortcut";
204-
return false;
205-
}
206-
207-
// Check pin state with newly created shortcut.
208-
auto pinned = IsShortcutPinnedWin10(shortcut_path->file_path());
209-
if (!pinned) {
210-
LOG(ERROR) << __func__ << " Can't use pin method.";
211-
return false;
212-
}
213-
214-
return pinned.value();
215-
}
216-
217106
// All args could be empty when we want to pin default profile's shortcut.
218-
void PinToTaskbarImpl(const base::FilePath& profile_path,
107+
bool PinToTaskbarImpl(const base::FilePath& profile_path,
219108
const std::u16string& profile_name,
220109
const std::wstring& aumid) {
221110
base::FilePath chrome_exe;
222-
base::PathService::Get(base::FILE_EXE, &chrome_exe);
111+
if (!base::PathService::Get(base::FILE_EXE, &chrome_exe))
112+
return false;
223113

224114
ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
225115
ShellUtil::AddDefaultShortcutProperties(chrome_exe, &properties);
@@ -243,131 +133,89 @@ void PinToTaskbarImpl(const base::FilePath& profile_path,
243133
GetShortcutPath(ExtractShortcutNameFromProperties(properties));
244134
if (!shortcut_path) {
245135
LOG(ERROR) << __func__ << " failed to get shortcut path";
246-
return;
136+
return false;
247137
}
248138

249139
if (!CreateShortcut(properties, shortcut_path->file_path())) {
250140
LOG(ERROR) << __func__ << " Failed to create shortcut";
251-
return;
252-
}
253-
254-
// Check pin state with newly created shortcut.
255-
auto pinned = IsShortcutPinnedWin10(shortcut_path->file_path());
256-
if (!pinned) {
257-
LOG(ERROR) << __func__ << " Can't use pin method.";
258-
return;
259-
}
260-
261-
// Don't try to pin again when it's already pinned.
262-
if (pinned.value())
263-
return;
264-
265-
PinShortcutWin10(shortcut_path->file_path());
266-
}
267-
268-
bool HasTaskbarAnyPinnedBraveShortcuts(
269-
const std::vector<std::tuple<base::FilePath, std::u16string, std::wstring>>&
270-
profile_attrs) {
271-
base::FilePath chrome_exe;
272-
base::PathService::Get(base::FILE_EXE, &chrome_exe);
273-
274-
ShellUtil::ShortcutProperties properties(ShellUtil::CURRENT_USER);
275-
ShellUtil::AddDefaultShortcutProperties(chrome_exe, &properties);
276-
for (const auto& attr : profile_attrs) {
277-
const auto profile_path = std::get<0>(attr);
278-
const auto profile_name = std::get<1>(attr);
279-
const auto profile_aumid = std::get<2>(attr);
280-
if (!profile_path.empty()) {
281-
properties.set_arguments(
282-
profiles::internal::CreateProfileShortcutFlags(profile_path));
283-
properties.set_shortcut_name(
284-
profiles::internal::GetShortcutFilenameForProfile(profile_name));
285-
properties.set_app_id(profile_aumid);
286-
}
287-
288-
if (IsShortcutPinned(properties))
289-
return true;
141+
return false;
290142
}
291143

292-
return false;
144+
return PinShortcutToTaskbar(shortcut_path->file_path());
293145
}
294146

295-
void DoPinToTaskbar(Profile* profile) {
147+
void DoPinToTaskbar(const base::FilePath& profile_path,
148+
base::OnceCallback<void(bool)> callback) {
296149
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
297150

298-
base::FilePath profile_path;
299151
std::u16string profile_name;
300152
std::wstring aumid;
301-
if (profile) {
153+
if (!profile_path.empty()) {
302154
ProfileManager* profile_manager = g_browser_process->profile_manager();
303155
ProfileAttributesEntry* entry =
304156
profile_manager->GetProfileAttributesStorage()
305-
.GetProfileAttributesWithPath(profile->GetPath());
306-
profile_path = profile->GetPath();
157+
.GetProfileAttributesWithPath(profile_path);
307158
profile_name = entry->GetName();
308159
aumid = shell_integration::win::GetAppUserModelIdForBrowser(profile_path);
309160
}
310161

311162
base::ThreadPool::CreateCOMSTATaskRunner({base::MayBlock()})
312-
->PostTask(FROM_HERE, base::BindOnce(&PinToTaskbarImpl, profile_path,
313-
profile_name, aumid));
163+
->PostTaskAndReplyWithResult(
164+
FROM_HERE,
165+
base::BindOnce(&PinToTaskbarImpl, profile_path, profile_name, aumid),
166+
std::move(callback));
314167
}
315168

316-
bool CanPinToTaskbar() {
317-
base::FilePath chrome_exe;
318-
if (!base::PathService::Get(base::FILE_EXE, &chrome_exe))
319-
return false;
169+
} // namespace
320170

321-
// TODO(simonhong): Support win7/8
322-
if (base::win::GetVersion() < base::win::Version::WIN10_RS5)
323-
return false;
171+
namespace shell_integration::win {
324172

325-
return true;
326-
}
173+
void PinToTaskbar(Profile* profile,
174+
base::OnceCallback<void(bool)> result_callback) {
175+
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
327176

328-
} // namespace
177+
if (!CanPinShortcutToTaskbar()) {
178+
std::move(result_callback).Run(false);
179+
return;
180+
}
329181

330-
namespace shell_integration::win {
182+
base::FilePath profile_path;
183+
if (profile)
184+
profile_path = profile->GetPath();
331185

332-
void PinToTaskbar(Profile* profile) {
333-
// Disable pin-to-taskabar uitll we have checkbox to ask the user to use it.
334-
return;
186+
// TODO(simonhong): handle connection error state if caller wants.
187+
GetIsPinnedToTaskbarState(
188+
base::DoNothing(),
189+
base::BindOnce(
190+
[](const base::FilePath& profile_path,
191+
base::OnceCallback<void(bool)> result_callback, bool succeeded,
192+
bool is_pinned_to_taskbar) {
193+
if (succeeded && is_pinned_to_taskbar) {
194+
// Early return. Already pinned.
195+
std::move(result_callback).Run(true);
196+
return;
197+
}
198+
DoPinToTaskbar(profile_path, std::move(result_callback));
199+
},
200+
std::move(profile_path), std::move(result_callback)));
201+
}
335202

203+
void IsShortcutPinned(base::OnceCallback<void(bool)> result_callback) {
336204
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
337205

338-
if (!CanPinToTaskbar())
206+
if (!CanPinShortcutToTaskbar()) {
207+
std::move(result_callback).Run(false);
339208
return;
340-
341-
// At the very early stage, |g_browser_process| or its profile_manager
342-
// are not initialzied yet. In that case, skip checking existing pin state.
343-
std::vector<std::tuple<base::FilePath, std::u16string, std::wstring>>
344-
profile_attrs;
345-
// Gather data that is available on UI thread and pass it.
346-
if (g_browser_process && g_browser_process->profile_manager()) {
347-
for (const auto* entry : g_browser_process->profile_manager()
348-
->GetProfileAttributesStorage()
349-
.GetAllProfilesAttributes()) {
350-
profile_attrs.push_back(
351-
std::make_tuple(entry->GetPath(), entry->GetName(),
352-
win::GetAppUserModelIdForBrowser(entry->GetPath())));
353-
}
354209
}
355210

356-
base::ThreadPool::CreateCOMSTATaskRunner({base::MayBlock()})
357-
->PostTaskAndReplyWithResult(
358-
FROM_HERE,
359-
base::BindOnce(&HasTaskbarAnyPinnedBraveShortcuts,
360-
std::move(profile_attrs)),
361-
base::BindOnce(
362-
[](Profile* profile, bool has_pin) {
363-
if (has_pin) {
364-
VLOG(2) << " Taskbar has already pinned brave shortcuts";
365-
return;
366-
}
367-
DoPinToTaskbar(profile);
368-
},
369-
profile));
370-
return;
211+
GetIsPinnedToTaskbarState(
212+
base::DoNothing(),
213+
base::BindOnce(
214+
[](base::OnceCallback<void(bool)> result_callback, bool succeeded,
215+
bool is_pinned_to_taskbar) {
216+
std::move(result_callback).Run(succeeded && is_pinned_to_taskbar);
217+
},
218+
std::move(result_callback)));
371219
}
372220

373221
} // namespace shell_integration::win

browser/brave_shell_integration_win.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,21 @@
66
#ifndef BRAVE_BROWSER_BRAVE_SHELL_INTEGRATION_WIN_H_
77
#define BRAVE_BROWSER_BRAVE_SHELL_INTEGRATION_WIN_H_
88

9+
#include "base/callback.h"
10+
#include "base/callback_helpers.h"
11+
912
class Profile;
1013

1114
namespace shell_integration::win {
1215

1316
// Pin profile-specific shortcut when |profile| is non-null.
14-
void PinToTaskbar(Profile* profile = nullptr);
17+
void PinToTaskbar(
18+
Profile* profile = nullptr,
19+
base::OnceCallback<void(bool)> result_callback = base::DoNothing());
20+
21+
// Returns true when taskbar has any shortcuts(default or profile-specific
22+
// ones).
23+
void IsShortcutPinned(base::OnceCallback<void(bool)> result_callback);
1524

1625
} // namespace shell_integration::win
1726

0 commit comments

Comments
 (0)