fix: enable navigator.setAppBadge/clearAppBadge (#27067)
This commit is contained in:
		
					parent
					
						
							
								8b74361b0c
							
						
					
				
			
			
				commit
				
					
						c5a41defbd
					
				
			
		
					 19 changed files with 481 additions and 41 deletions
				
			
		
							
								
								
									
										4
									
								
								docs/api/app.md
									
										
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										4
									
								
								docs/api/app.md
									
										
									
									
									
										
										
										Normal file → Executable file
									
								
							|  | @ -1174,9 +1174,9 @@ For `infoType` equal to `basic`: | |||
| 
 | ||||
| Using `basic` should be preferred if only basic information like `vendorId` or `driverId` is needed. | ||||
| 
 | ||||
| ### `app.setBadgeCount(count)` _Linux_ _macOS_ | ||||
| ### `app.setBadgeCount([count])` _Linux_ _macOS_ | ||||
| 
 | ||||
| * `count` Integer | ||||
| * `count` Integer (optional) - If a value is provided, set the badge to the provided value otherwise, on macOS, display a plain white dot (e.g. unknown number of notifications). On Linux, if a value is not provided the badge will not display. | ||||
| 
 | ||||
| Returns `Boolean` - Whether the call succeeded. | ||||
| 
 | ||||
|  |  | |||
|  | @ -83,5 +83,18 @@ | |||
|   <message name="IDS_DOWNLOAD_MORE_ACTIONS" | ||||
|           desc="Tooltip of a button on the downloads page that shows a menu with actions like 'Open downloads folder' or 'Clear all'"> | ||||
|     More actions | ||||
| </message> | ||||
|   </message> | ||||
|   <!-- Badging --> | ||||
|   <message name="IDS_SATURATED_BADGE_CONTENT" desc="The content to display when the application's badge is too large to display to indicate that the badge is more than a given maximum. This string should be as short as possible, preferably only one character beyond the content"> | ||||
|     <ph name="MAXIMUM_VALUE">$1<ex>99</ex></ph>+ | ||||
|   </message> | ||||
|   <message name="IDS_BADGE_UNREAD_NOTIFICATIONS_SATURATED" desc="The accessibility text which will be read by a screen reader when the notification count is too large to display (e.g. greater than 99)."> | ||||
|     {MAX_UNREAD_NOTIFICATIONS, plural, =1 {More than 1 unread notification} other {More than # unread notifications}} | ||||
|   </message> | ||||
|   <message name="IDS_BADGE_UNREAD_NOTIFICATIONS_UNSPECIFIED" desc="The accessibility text which will be read by a screen reader when there are some unspecified number of notifications, or user attention is required"> | ||||
|     Unread Notifications | ||||
|   </message> | ||||
|   <message name="IDS_BADGE_UNREAD_NOTIFICATIONS" desc="The accessibility text which will be read by a screen reader when there are notifcatications"> | ||||
|     {UNREAD_NOTIFICATIONS, plural, =1 {1 Unread Notification} other {# Unread Notifications}} | ||||
|   </message>   | ||||
| </grit-part> | ||||
|  |  | |||
|  | @ -328,6 +328,10 @@ filenames = { | |||
|     "shell/browser/api/ui_event.h", | ||||
|     "shell/browser/auto_updater.cc", | ||||
|     "shell/browser/auto_updater.h", | ||||
|     "shell/browser/badging/badge_manager.cc", | ||||
|     "shell/browser/badging/badge_manager.h", | ||||
|     "shell/browser/badging/badge_manager_factory.cc", | ||||
|     "shell/browser/badging/badge_manager_factory.h", | ||||
|     "shell/browser/browser.cc", | ||||
|     "shell/browser/browser.h", | ||||
|     "shell/browser/browser_observer.h", | ||||
|  |  | |||
							
								
								
									
										81
									
								
								shell/browser/badging/badge_manager.cc
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										81
									
								
								shell/browser/badging/badge_manager.cc
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,81 @@ | |||
| // Copyright 2018 The Chromium Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style license that can be
 | ||||
| // found in the LICENSE file.
 | ||||
| 
 | ||||
| #include "shell/browser/badging/badge_manager.h" | ||||
| 
 | ||||
| #include <tuple> | ||||
| #include <utility> | ||||
| 
 | ||||
| #include "base/i18n/number_formatting.h" | ||||
| #include "base/strings/utf_string_conversions.h" | ||||
| #include "build/build_config.h" | ||||
| #include "content/public/browser/browser_task_traits.h" | ||||
| #include "content/public/browser/browser_thread.h" | ||||
| #include "content/public/browser/render_frame_host.h" | ||||
| #include "content/public/browser/render_process_host.h" | ||||
| #include "content/public/browser/web_contents.h" | ||||
| #include "shell/browser/badging/badge_manager_factory.h" | ||||
| #include "shell/browser/browser.h" | ||||
| #include "ui/base/l10n/l10n_util.h" | ||||
| #include "ui/strings/grit/ui_strings.h" | ||||
| 
 | ||||
| namespace badging { | ||||
| 
 | ||||
| BadgeManager::BadgeManager() = default; | ||||
| BadgeManager::~BadgeManager() = default; | ||||
| 
 | ||||
| // static
 | ||||
| void BadgeManager::BindFrameReceiver( | ||||
|     content::RenderFrameHost* frame, | ||||
|     mojo::PendingReceiver<blink::mojom::BadgeService> receiver) { | ||||
|   DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||||
| 
 | ||||
|   auto* browser_context = | ||||
|       content::WebContents::FromRenderFrameHost(frame)->GetBrowserContext(); | ||||
| 
 | ||||
|   auto* badge_manager = | ||||
|       badging::BadgeManagerFactory::GetInstance()->GetForBrowserContext( | ||||
|           browser_context); | ||||
|   if (!badge_manager) | ||||
|     return; | ||||
| 
 | ||||
|   auto context = std::make_unique<FrameBindingContext>( | ||||
|       frame->GetProcess()->GetID(), frame->GetRoutingID()); | ||||
| 
 | ||||
|   badge_manager->receivers_.Add(badge_manager, std::move(receiver), | ||||
|                                 std::move(context)); | ||||
| } | ||||
| 
 | ||||
| std::string BadgeManager::GetBadgeString(base::Optional<int> badge_content) { | ||||
|   if (!badge_content) | ||||
|     return "•"; | ||||
| 
 | ||||
|   if (badge_content > kMaxBadgeContent) { | ||||
|     return base::UTF16ToUTF8(l10n_util::GetStringFUTF16( | ||||
|         IDS_SATURATED_BADGE_CONTENT, base::FormatNumber(kMaxBadgeContent))); | ||||
|   } | ||||
| 
 | ||||
|   return base::UTF16ToUTF8(base::FormatNumber(badge_content.value())); | ||||
| } | ||||
| 
 | ||||
| void BadgeManager::SetBadge(blink::mojom::BadgeValuePtr mojo_value) { | ||||
|   if (mojo_value->is_number() && mojo_value->get_number() == 0) { | ||||
|     mojo::ReportBadMessage( | ||||
|         "|value| should not be zero when it is |number| (ClearBadge should be " | ||||
|         "called instead)!"); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   base::Optional<int> value = | ||||
|       mojo_value->is_flag() ? base::nullopt | ||||
|                             : base::make_optional(mojo_value->get_number()); | ||||
| 
 | ||||
|   electron::Browser::Get()->SetBadgeCount(value); | ||||
| } | ||||
| 
 | ||||
| void BadgeManager::ClearBadge() { | ||||
|   electron::Browser::Get()->SetBadgeCount(0); | ||||
| } | ||||
| 
 | ||||
| }  // namespace badging
 | ||||
							
								
								
									
										90
									
								
								shell/browser/badging/badge_manager.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								shell/browser/badging/badge_manager.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,90 @@ | |||
| // Copyright 2018 The Chromium Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style license that can be
 | ||||
| // found in the LICENSE file.
 | ||||
| 
 | ||||
| #ifndef SHELL_BROWSER_BADGING_BADGE_MANAGER_H_ | ||||
| #define SHELL_BROWSER_BADGING_BADGE_MANAGER_H_ | ||||
| 
 | ||||
| #include <map> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "base/macros.h" | ||||
| #include "base/optional.h" | ||||
| #include "components/keyed_service/core/keyed_service.h" | ||||
| #include "mojo/public/cpp/bindings/receiver_set.h" | ||||
| #include "third_party/blink/public/mojom/badging/badging.mojom.h" | ||||
| #include "url/gurl.h" | ||||
| 
 | ||||
| namespace content { | ||||
| class RenderFrameHost; | ||||
| class RenderProcessHost; | ||||
| }  // namespace content
 | ||||
| 
 | ||||
| namespace badging { | ||||
| 
 | ||||
| // The maximum value of badge contents before saturation occurs.
 | ||||
| constexpr int kMaxBadgeContent = 99; | ||||
| 
 | ||||
| // Maintains a record of badge contents and dispatches badge changes to a
 | ||||
| // delegate.
 | ||||
| class BadgeManager : public KeyedService, public blink::mojom::BadgeService { | ||||
|  public: | ||||
|   BadgeManager(); | ||||
|   ~BadgeManager() override; | ||||
| 
 | ||||
|   static void BindFrameReceiver( | ||||
|       content::RenderFrameHost* frame, | ||||
|       mojo::PendingReceiver<blink::mojom::BadgeService> receiver); | ||||
| 
 | ||||
|   // Determines the text to put on the badge based on some badge_content.
 | ||||
|   static std::string GetBadgeString(base::Optional<int> badge_content); | ||||
| 
 | ||||
|  private: | ||||
|   // The BindingContext of a mojo request. Allows mojo calls to be tied back
 | ||||
|   // to the execution context they belong to without trusting the renderer for
 | ||||
|   // that information.  This is an abstract base class that different types of
 | ||||
|   // execution contexts derive.
 | ||||
|   class BindingContext { | ||||
|    public: | ||||
|     virtual ~BindingContext() = default; | ||||
|   }; | ||||
| 
 | ||||
|   // The BindingContext for Window execution contexts.
 | ||||
|   class FrameBindingContext final : public BindingContext { | ||||
|    public: | ||||
|     FrameBindingContext(int process_id, int frame_id) | ||||
|         : process_id_(process_id), frame_id_(frame_id) {} | ||||
|     ~FrameBindingContext() override = default; | ||||
| 
 | ||||
|     int GetProcessId() { return process_id_; } | ||||
|     int GetFrameId() { return frame_id_; } | ||||
| 
 | ||||
|    private: | ||||
|     int process_id_; | ||||
|     int frame_id_; | ||||
|   }; | ||||
| 
 | ||||
|   // blink::mojom::BadgeService:
 | ||||
|   // Note: These are private to stop them being called outside of mojo as they
 | ||||
|   // require a mojo binding context.
 | ||||
|   void SetBadge(blink::mojom::BadgeValuePtr value) override; | ||||
|   void ClearBadge() override; | ||||
| 
 | ||||
|   // All the mojo receivers for the BadgeManager. Keeps track of the
 | ||||
|   // render_frame the binding is associated with, so as to not have to rely
 | ||||
|   // on the renderer passing it in.
 | ||||
|   mojo::ReceiverSet<blink::mojom::BadgeService, std::unique_ptr<BindingContext>> | ||||
|       receivers_; | ||||
| 
 | ||||
|   // Delegate which handles actual setting and clearing of the badge.
 | ||||
|   // Note: This is currently only set on Windows and MacOS.
 | ||||
|   // std::unique_ptr<BadgeManagerDelegate> delegate_;
 | ||||
| 
 | ||||
|   DISALLOW_COPY_AND_ASSIGN(BadgeManager); | ||||
| }; | ||||
| 
 | ||||
| }  // namespace badging
 | ||||
| 
 | ||||
| #endif  // SHELL_BROWSER_BADGING_BADGE_MANAGER_H_
 | ||||
							
								
								
									
										41
									
								
								shell/browser/badging/badge_manager_factory.cc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								shell/browser/badging/badge_manager_factory.cc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| // Copyright 2018 The Chromium Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style license that can be
 | ||||
| // found in the LICENSE file.
 | ||||
| 
 | ||||
| #include "shell/browser/badging/badge_manager_factory.h" | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "base/bind.h" | ||||
| #include "base/memory/ptr_util.h" | ||||
| #include "base/memory/singleton.h" | ||||
| #include "components/keyed_service/content/browser_context_dependency_manager.h" | ||||
| #include "shell/browser/badging/badge_manager.h" | ||||
| 
 | ||||
| namespace badging { | ||||
| 
 | ||||
| // static
 | ||||
| BadgeManager* BadgeManagerFactory::GetForBrowserContext( | ||||
|     content::BrowserContext* context) { | ||||
|   return static_cast<badging::BadgeManager*>( | ||||
|       GetInstance()->GetServiceForBrowserContext(context, true)); | ||||
| } | ||||
| 
 | ||||
| // static
 | ||||
| BadgeManagerFactory* BadgeManagerFactory::GetInstance() { | ||||
|   return base::Singleton<BadgeManagerFactory>::get(); | ||||
| } | ||||
| 
 | ||||
| BadgeManagerFactory::BadgeManagerFactory() | ||||
|     : BrowserContextKeyedServiceFactory( | ||||
|           "BadgeManager", | ||||
|           BrowserContextDependencyManager::GetInstance()) {} | ||||
| 
 | ||||
| BadgeManagerFactory::~BadgeManagerFactory() {} | ||||
| 
 | ||||
| KeyedService* BadgeManagerFactory::BuildServiceInstanceFor( | ||||
|     content::BrowserContext* context) const { | ||||
|   return new BadgeManager(); | ||||
| } | ||||
| 
 | ||||
| }  // namespace badging
 | ||||
							
								
								
									
										44
									
								
								shell/browser/badging/badge_manager_factory.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								shell/browser/badging/badge_manager_factory.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| // Copyright 2018 The Chromium Authors. All rights reserved.
 | ||||
| // Use of this source code is governed by a BSD-style license that can be
 | ||||
| // found in the LICENSE file.
 | ||||
| 
 | ||||
| #ifndef SHELL_BROWSER_BADGING_BADGE_MANAGER_FACTORY_H_ | ||||
| #define SHELL_BROWSER_BADGING_BADGE_MANAGER_FACTORY_H_ | ||||
| 
 | ||||
| #include "base/macros.h" | ||||
| #include "components/keyed_service/content/browser_context_keyed_service_factory.h" | ||||
| 
 | ||||
| namespace base { | ||||
| template <typename T> | ||||
| struct DefaultSingletonTraits; | ||||
| } | ||||
| 
 | ||||
| namespace badging { | ||||
| 
 | ||||
| class BadgeManager; | ||||
| 
 | ||||
| // Singleton that provides access to context specific BadgeManagers.
 | ||||
| class BadgeManagerFactory : public BrowserContextKeyedServiceFactory { | ||||
|  public: | ||||
|   // Gets the BadgeManager for the specified context
 | ||||
|   static BadgeManager* GetForBrowserContext(content::BrowserContext* context); | ||||
| 
 | ||||
|   // Returns the BadgeManagerFactory singleton.
 | ||||
|   static BadgeManagerFactory* GetInstance(); | ||||
| 
 | ||||
|  private: | ||||
|   friend struct base::DefaultSingletonTraits<BadgeManagerFactory>; | ||||
| 
 | ||||
|   BadgeManagerFactory(); | ||||
|   ~BadgeManagerFactory() override; | ||||
| 
 | ||||
|   // BrowserContextKeyedServiceFactory
 | ||||
|   KeyedService* BuildServiceInstanceFor( | ||||
|       content::BrowserContext* context) const override; | ||||
| 
 | ||||
|   DISALLOW_COPY_AND_ASSIGN(BadgeManagerFactory); | ||||
| }; | ||||
| 
 | ||||
| }  // namespace badging
 | ||||
| 
 | ||||
| #endif  // SHELL_BROWSER_BADGING_BADGE_MANAGER_FACTORY_H_
 | ||||
							
								
								
									
										0
									
								
								shell/browser/browser.cc
									
										
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								shell/browser/browser.cc
									
										
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										12
									
								
								shell/browser/browser.h
									
										
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										12
									
								
								shell/browser/browser.h
									
										
									
									
									
										
										
										Normal file → Executable file
									
								
							|  | @ -23,6 +23,7 @@ | |||
| #if defined(OS_WIN) | ||||
| #include <windows.h> | ||||
| #include "base/files/file_path.h" | ||||
| #include "shell/browser/ui/win/taskbar_host.h" | ||||
| #endif | ||||
| 
 | ||||
| #if defined(OS_MAC) | ||||
|  | @ -107,7 +108,7 @@ class Browser : public WindowListObserver { | |||
| #endif | ||||
| 
 | ||||
|   // Set/Get the badge count.
 | ||||
|   bool SetBadgeCount(int count); | ||||
|   bool SetBadgeCount(base::Optional<int> count); | ||||
|   int GetBadgeCount(); | ||||
| 
 | ||||
| #if defined(OS_WIN) | ||||
|  | @ -364,6 +365,15 @@ class Browser : public WindowListObserver { | |||
|   base::DictionaryValue about_panel_options_; | ||||
| #endif | ||||
| 
 | ||||
| #if defined(OS_WIN) | ||||
|   void UpdateBadgeContents(HWND hwnd, | ||||
|                            const base::Optional<std::string>& badge_content, | ||||
|                            const std::string& badge_alt_string); | ||||
| 
 | ||||
|   // In charge of running taskbar related APIs.
 | ||||
|   TaskbarHost taskbar_host_; | ||||
| #endif | ||||
| 
 | ||||
|   DISALLOW_COPY_AND_ASSIGN(Browser); | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -130,10 +130,10 @@ base::string16 Browser::GetApplicationNameForProtocol(const GURL& url) { | |||
|   return base::ASCIIToUTF16(GetXdgAppOutput(argv).value_or(std::string())); | ||||
| } | ||||
| 
 | ||||
| bool Browser::SetBadgeCount(int count) { | ||||
|   if (IsUnityRunning()) { | ||||
|     unity::SetDownloadCount(count); | ||||
|     badge_count_ = count; | ||||
| bool Browser::SetBadgeCount(base::Optional<int> count) { | ||||
|   if (IsUnityRunning() && count.has_value()) { | ||||
|     unity::SetDownloadCount(count.value()); | ||||
|     badge_count_ = count.value(); | ||||
|     return true; | ||||
|   } else { | ||||
|     return false; | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ | |||
| #include "base/strings/string_number_conversions.h" | ||||
| #include "base/strings/sys_string_conversions.h" | ||||
| #include "net/base/mac/url_conversions.h" | ||||
| #include "shell/browser/badging/badge_manager.h" | ||||
| #include "shell/browser/mac/dict_util.h" | ||||
| #include "shell/browser/mac/electron_application.h" | ||||
| #include "shell/browser/mac/electron_application_delegate.h" | ||||
|  | @ -219,9 +220,15 @@ base::string16 Browser::GetApplicationNameForProtocol(const GURL& url) { | |||
| 
 | ||||
| void Browser::SetAppUserModelID(const base::string16& name) {} | ||||
| 
 | ||||
| bool Browser::SetBadgeCount(int count) { | ||||
|   DockSetBadgeText(count != 0 ? base::NumberToString(count) : ""); | ||||
|   badge_count_ = count; | ||||
| bool Browser::SetBadgeCount(base::Optional<int> count) { | ||||
|   DockSetBadgeText(!count.has_value() || count.value() != 0 | ||||
|                        ? badging::BadgeManager::GetBadgeString(count) | ||||
|                        : ""); | ||||
|   if (count.has_value()) { | ||||
|     badge_count_ = count.value(); | ||||
|   } else { | ||||
|     badge_count_ = 0; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										101
									
								
								shell/browser/browser_win.cc
									
										
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										101
									
								
								shell/browser/browser_win.cc
									
										
									
									
									
										
										
										Normal file → Executable file
									
								
							|  | @ -28,16 +28,21 @@ | |||
| #include "chrome/browser/icon_manager.h" | ||||
| #include "electron/electron_version.h" | ||||
| #include "shell/browser/api/electron_api_app.h" | ||||
| #include "shell/browser/badging/badge_manager.h" | ||||
| #include "shell/browser/electron_browser_main_parts.h" | ||||
| #include "shell/browser/ui/message_box.h" | ||||
| #include "shell/browser/ui/win/jump_list.h" | ||||
| #include "shell/browser/window_list.h" | ||||
| #include "shell/common/application_info.h" | ||||
| #include "shell/common/gin_converters/file_path_converter.h" | ||||
| #include "shell/common/gin_converters/image_converter.h" | ||||
| #include "shell/common/gin_helper/arguments.h" | ||||
| #include "shell/common/gin_helper/dictionary.h" | ||||
| #include "shell/common/skia_util.h" | ||||
| #include "skia/ext/legacy_display_globals.h" | ||||
| #include "ui/base/l10n/l10n_util.h" | ||||
| #include "ui/events/keycodes/keyboard_code_conversion_win.h" | ||||
| #include "ui/strings/grit/ui_strings.h" | ||||
| 
 | ||||
| namespace electron { | ||||
| 
 | ||||
|  | @ -584,8 +589,100 @@ v8::Local<v8::Promise> Browser::GetApplicationInfoForProtocol( | |||
|   return handle; | ||||
| } | ||||
| 
 | ||||
| bool Browser::SetBadgeCount(int count) { | ||||
|   return false; | ||||
| bool Browser::SetBadgeCount(base::Optional<int> count) { | ||||
|   base::Optional<std::string> badge_content; | ||||
|   if (count.has_value() && count.value() == 0) { | ||||
|     badge_content = base::nullopt; | ||||
|   } else { | ||||
|     badge_content = badging::BadgeManager::GetBadgeString(count); | ||||
|   } | ||||
| 
 | ||||
|   // There are 3 different cases when the badge has a value:
 | ||||
|   // 1. |contents| is between 1 and 99 inclusive => Set the accessibility text
 | ||||
|   //    to a pluralized notification count (e.g. 4 Unread Notifications).
 | ||||
|   // 2. |contents| is greater than 99 => Set the accessibility text to
 | ||||
|   //    More than |kMaxBadgeContent| unread notifications, so the
 | ||||
|   //    accessibility text matches what is displayed on the badge (e.g. More
 | ||||
|   //    than 99 notifications).
 | ||||
|   // 3. The badge is set to 'flag' => Set the accessibility text to something
 | ||||
|   //    less specific (e.g. Unread Notifications).
 | ||||
|   std::string badge_alt_string; | ||||
|   if (count.has_value()) { | ||||
|     badge_count_ = count.value(); | ||||
|     badge_alt_string = (uint64_t)badge_count_ <= badging::kMaxBadgeContent | ||||
|                            // Case 1.
 | ||||
|                            ? l10n_util::GetPluralStringFUTF8( | ||||
|                                  IDS_BADGE_UNREAD_NOTIFICATIONS, badge_count_) | ||||
|                            // Case 2.
 | ||||
|                            : l10n_util::GetPluralStringFUTF8( | ||||
|                                  IDS_BADGE_UNREAD_NOTIFICATIONS_SATURATED, | ||||
|                                  badging::kMaxBadgeContent); | ||||
|   } else { | ||||
|     // Case 3.
 | ||||
|     badge_alt_string = | ||||
|         l10n_util::GetStringUTF8(IDS_BADGE_UNREAD_NOTIFICATIONS_UNSPECIFIED); | ||||
|     badge_count_ = 0; | ||||
|   } | ||||
|   for (auto* window : WindowList::GetWindows()) { | ||||
|     // On Windows set the badge on the first window found.
 | ||||
|     UpdateBadgeContents(window->GetAcceleratedWidget(), badge_content, | ||||
|                         badge_alt_string); | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| void Browser::UpdateBadgeContents( | ||||
|     HWND hwnd, | ||||
|     const base::Optional<std::string>& badge_content, | ||||
|     const std::string& badge_alt_string) { | ||||
|   SkBitmap badge; | ||||
|   if (badge_content) { | ||||
|     std::string content = badge_content.value(); | ||||
|     constexpr int kOverlayIconSize = 16; | ||||
|     // This is the color used by the Windows 10 Badge API, for platform
 | ||||
|     // consistency.
 | ||||
|     constexpr int kBackgroundColor = SkColorSetRGB(0x26, 0x25, 0x2D); | ||||
|     constexpr int kForegroundColor = SK_ColorWHITE; | ||||
|     constexpr int kRadius = kOverlayIconSize / 2; | ||||
|     // The minimum gap to have between our content and the edge of the badge.
 | ||||
|     constexpr int kMinMargin = 3; | ||||
|     // The amount of space we have to render the icon.
 | ||||
|     constexpr int kMaxBounds = kOverlayIconSize - 2 * kMinMargin; | ||||
|     constexpr int kMaxTextSize = 24;  // Max size for our text.
 | ||||
|     constexpr int kMinTextSize = 7;   // Min size for our text.
 | ||||
| 
 | ||||
|     badge.allocN32Pixels(kOverlayIconSize, kOverlayIconSize); | ||||
|     SkCanvas canvas(badge, skia::LegacyDisplayGlobals::GetSkSurfaceProps()); | ||||
| 
 | ||||
|     SkPaint paint; | ||||
|     paint.setAntiAlias(true); | ||||
|     paint.setColor(kBackgroundColor); | ||||
| 
 | ||||
|     canvas.clear(SK_ColorTRANSPARENT); | ||||
|     canvas.drawCircle(kRadius, kRadius, kRadius, paint); | ||||
| 
 | ||||
|     paint.reset(); | ||||
|     paint.setColor(kForegroundColor); | ||||
| 
 | ||||
|     SkFont font; | ||||
| 
 | ||||
|     SkRect bounds; | ||||
|     int text_size = kMaxTextSize; | ||||
|     // Find the largest |text_size| larger than |kMinTextSize| in which
 | ||||
|     // |content| fits into our 16x16px icon, with margins.
 | ||||
|     do { | ||||
|       font.setSize(text_size--); | ||||
|       font.measureText(content.c_str(), content.size(), SkTextEncoding::kUTF8, | ||||
|                        &bounds); | ||||
|     } while (text_size >= kMinTextSize && | ||||
|              (bounds.width() > kMaxBounds || bounds.height() > kMaxBounds)); | ||||
| 
 | ||||
|     canvas.drawSimpleText( | ||||
|         content.c_str(), content.size(), SkTextEncoding::kUTF8, | ||||
|         kRadius - bounds.width() / 2 - bounds.x(), | ||||
|         kRadius - bounds.height() / 2 - bounds.y(), font, paint); | ||||
|   } | ||||
|   taskbar_host_.SetOverlayIcon(hwnd, badge, badge_alt_string); | ||||
| } | ||||
| 
 | ||||
| void Browser::SetLoginItemSettings(LoginItemSettings settings) { | ||||
|  |  | |||
|  | @ -65,6 +65,7 @@ | |||
| #include "shell/browser/api/electron_api_session.h" | ||||
| #include "shell/browser/api/electron_api_web_contents.h" | ||||
| #include "shell/browser/api/electron_api_web_request.h" | ||||
| #include "shell/browser/badging/badge_manager.h" | ||||
| #include "shell/browser/child_web_contents_tracker.h" | ||||
| #include "shell/browser/electron_autofill_driver_factory.h" | ||||
| #include "shell/browser/electron_browser_context.h" | ||||
|  | @ -1622,12 +1623,6 @@ void ElectronBrowserClient::BindHostReceiverForRenderer( | |||
| #endif | ||||
| } | ||||
| 
 | ||||
| void BindBadgeManagerFrameReceiver( | ||||
|     content::RenderFrameHost* frame, | ||||
|     mojo::PendingReceiver<blink::mojom::BadgeService> receiver) { | ||||
|   LOG(WARNING) << "The Chromium Badging API is not available in Electron"; | ||||
| } | ||||
| 
 | ||||
| void BindElectronBrowser( | ||||
|     content::RenderFrameHost* frame_host, | ||||
|     mojo::PendingReceiver<electron::mojom::ElectronBrowser> receiver) { | ||||
|  | @ -1671,7 +1666,7 @@ void ElectronBrowserClient::RegisterBrowserInterfaceBindersForFrame( | |||
|   map->Add<network_hints::mojom::NetworkHintsHandler>( | ||||
|       base::BindRepeating(&BindNetworkHintsHandler)); | ||||
|   map->Add<blink::mojom::BadgeService>( | ||||
|       base::BindRepeating(&BindBadgeManagerFrameReceiver)); | ||||
|       base::BindRepeating(&badging::BadgeManager::BindFrameReceiver)); | ||||
|   map->Add<electron::mojom::ElectronBrowser>( | ||||
|       base::BindRepeating(&BindElectronBrowser)); | ||||
| #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) | ||||
|  |  | |||
|  | @ -1209,7 +1209,9 @@ void NativeWindowViews::SetProgressBar(double progress, | |||
| void NativeWindowViews::SetOverlayIcon(const gfx::Image& overlay, | ||||
|                                        const std::string& description) { | ||||
| #if defined(OS_WIN) | ||||
|   taskbar_host_.SetOverlayIcon(GetAcceleratedWidget(), overlay, description); | ||||
|   SkBitmap overlay_bitmap = overlay.AsBitmap(); | ||||
|   taskbar_host_.SetOverlayIcon(GetAcceleratedWidget(), overlay_bitmap, | ||||
|                                description); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -166,13 +166,12 @@ bool TaskbarHost::SetProgressBar(HWND window, | |||
| } | ||||
| 
 | ||||
| bool TaskbarHost::SetOverlayIcon(HWND window, | ||||
|                                  const gfx::Image& overlay, | ||||
|                                  const SkBitmap& overlay, | ||||
|                                  const std::string& text) { | ||||
|   if (!InitializeTaskbar()) | ||||
|     return false; | ||||
| 
 | ||||
|   base::win::ScopedHICON icon( | ||||
|       IconUtil::CreateHICONFromSkBitmap(overlay.AsBitmap())); | ||||
|   base::win::ScopedHICON icon(IconUtil::CreateHICONFromSkBitmap(overlay)); | ||||
|   return SUCCEEDED(taskbar_->SetOverlayIcon(window, icon.get(), | ||||
|                                             base::UTF8ToUTF16(text).c_str())); | ||||
| } | ||||
|  |  | |||
|  | @ -48,7 +48,7 @@ class TaskbarHost { | |||
| 
 | ||||
|   // Set the overlay icon in taskbar.
 | ||||
|   bool SetOverlayIcon(HWND window, | ||||
|                       const gfx::Image& overlay, | ||||
|                       const SkBitmap& overlay, | ||||
|                       const std::string& text); | ||||
| 
 | ||||
|   // Set the region of the window to show as a thumbnail in taskbar.
 | ||||
|  |  | |||
|  | @ -551,46 +551,42 @@ describe('app module', () => { | |||
|     const platformIsNotSupported = | ||||
|         (process.platform === 'win32') || | ||||
|         (process.platform === 'linux' && !app.isUnityRunning()); | ||||
|     const platformIsSupported = !platformIsNotSupported; | ||||
| 
 | ||||
|     const expectedBadgeCount = 42; | ||||
| 
 | ||||
|     after(() => { app.badgeCount = 0; }); | ||||
| 
 | ||||
|     describe('on supported platform', () => { | ||||
|       it('with properties', () => { | ||||
|     ifdescribe(!platformIsNotSupported)('on supported platform', () => { | ||||
|       describe('with properties', () => { | ||||
|         it('sets a badge count', function () { | ||||
|           if (platformIsNotSupported) return this.skip(); | ||||
| 
 | ||||
|           app.badgeCount = expectedBadgeCount; | ||||
|           expect(app.badgeCount).to.equal(expectedBadgeCount); | ||||
|         }); | ||||
|       }); | ||||
| 
 | ||||
|       it('with functions', () => { | ||||
|         it('sets a badge count', function () { | ||||
|           if (platformIsNotSupported) return this.skip(); | ||||
| 
 | ||||
|       describe('with functions', () => { | ||||
|         it('sets a numerical badge count', function () { | ||||
|           app.setBadgeCount(expectedBadgeCount); | ||||
|           expect(app.getBadgeCount()).to.equal(expectedBadgeCount); | ||||
|         }); | ||||
|         it('sets an non numeric (dot) badge count', function () { | ||||
|           app.setBadgeCount(); | ||||
|           // Badge count should be zero when non numeric (dot) is requested
 | ||||
|           expect(app.getBadgeCount()).to.equal(0); | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('on unsupported platform', () => { | ||||
|       it('with properties', () => { | ||||
|     ifdescribe(process.platform !== 'win32' && platformIsNotSupported)('on unsupported platform', () => { | ||||
|       describe('with properties', () => { | ||||
|         it('does not set a badge count', function () { | ||||
|           if (platformIsSupported) return this.skip(); | ||||
| 
 | ||||
|           app.badgeCount = 9999; | ||||
|           expect(app.badgeCount).to.equal(0); | ||||
|         }); | ||||
|       }); | ||||
| 
 | ||||
|       it('with functions', () => { | ||||
|       describe('with functions', () => { | ||||
|         it('does not set a badge count)', function () { | ||||
|           if (platformIsSupported) return this.skip(); | ||||
| 
 | ||||
|           app.setBadgeCount(9999); | ||||
|           expect(app.getBadgeCount()).to.equal(0); | ||||
|         }); | ||||
|  |  | |||
|  | @ -1550,3 +1550,57 @@ describe('navigator.clipboard', () => { | |||
|     expect(clipboard).to.not.equal('Read permission denied.'); | ||||
|   }); | ||||
| }); | ||||
| 
 | ||||
| ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.setAppBadge/clearAppBadge', () => { | ||||
|   let w: BrowserWindow; | ||||
|   before(async () => { | ||||
|     w = new BrowserWindow({ | ||||
|       show: false | ||||
|     }); | ||||
|     await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html')); | ||||
|   }); | ||||
| 
 | ||||
|   const expectedBadgeCount = 42; | ||||
| 
 | ||||
|   const fireAppBadgeAction: any = (action: string, value: any) => { | ||||
|     return w.webContents.executeJavaScript(` | ||||
|       navigator.${action}AppBadge(${value}).then(() => 'success').catch(err => err.message)`);
 | ||||
|   }; | ||||
| 
 | ||||
|   // For some reason on macOS changing the badge count doesn't happen right away, so wait
 | ||||
|   // until it changes.
 | ||||
|   async function waitForBadgeCount (value: number) { | ||||
|     let badgeCount = app.getBadgeCount(); | ||||
|     while (badgeCount !== value) { | ||||
|       await new Promise(resolve => setTimeout(resolve, 10)); | ||||
|       badgeCount = app.getBadgeCount(); | ||||
|     } | ||||
|     return badgeCount; | ||||
|   } | ||||
| 
 | ||||
|   after(() => { | ||||
|     app.badgeCount = 0; | ||||
|     closeAllWindows(); | ||||
|   }); | ||||
| 
 | ||||
|   it('setAppBadge can set a numerical value', async () => { | ||||
|     const result = await fireAppBadgeAction('set', expectedBadgeCount); | ||||
|     expect(result).to.equal('success'); | ||||
|     expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount); | ||||
|   }); | ||||
| 
 | ||||
|   it('setAppBadge can set an empty(dot) value', async () => { | ||||
|     const result = await fireAppBadgeAction('set'); | ||||
|     expect(result).to.equal('success'); | ||||
|     expect(waitForBadgeCount(0)).to.eventually.equal(0); | ||||
|   }); | ||||
| 
 | ||||
|   it('clearAppBadge can clear a value', async () => { | ||||
|     let result = await fireAppBadgeAction('set', expectedBadgeCount); | ||||
|     expect(result).to.equal('success'); | ||||
|     expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount); | ||||
|     result = await fireAppBadgeAction('clear'); | ||||
|     expect(result).to.equal('success'); | ||||
|     expect(waitForBadgeCount(0)).to.eventually.equal(0); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
|  | @ -22,6 +22,13 @@ describe('chromium feature', () => { | |||
|       expect(() => { | ||||
|         navigator.setAppBadge(42); | ||||
|       }).to.not.throw(); | ||||
|       expect(() => { | ||||
|         // setAppBadge with no argument should show dot
 | ||||
|         navigator.setAppBadge(); | ||||
|       }).to.not.throw(); | ||||
|       expect(() => { | ||||
|         navigator.clearAppBadge(); | ||||
|       }).to.not.throw(); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 John Kleinschmidt
				John Kleinschmidt