Skip to content

Commit

Permalink
feat: enable navigator.setAppBadge/clearAppBadge
Browse files Browse the repository at this point in the history
  • Loading branch information
jkleinsc committed Jan 19, 2021
1 parent 5fb1095 commit 26bd5d0
Show file tree
Hide file tree
Showing 19 changed files with 531 additions and 27 deletions.
6 changes: 3 additions & 3 deletions docs/api/app.md 100644 → 100755
Expand Up @@ -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.

Expand All @@ -1188,7 +1188,7 @@ On macOS, it shows on the dock icon. On Linux, it only works for Unity launcher.
**Note:** Unity launcher requires the existence of a `.desktop` file to work,
for more information please read [Desktop Environment Integration][unity-requirement].

### `app.getBadgeCount()` _Linux_ _macOS_
### `app.getBadgeCount()`

Returns `Integer` - The current value displayed in the counter badge.

Expand Down
15 changes: 14 additions & 1 deletion electron_strings.grdp
Expand Up @@ -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>
4 changes: 4 additions & 0 deletions filenames.gni
Expand Up @@ -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",
Expand Down
89 changes: 89 additions & 0 deletions shell/browser/badging/badge_manager.cc
@@ -0,0 +1,89 @@
// 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());

#if defined(OS_WIN)
electron::Browser::Get()->SetBadgeCount(value, true);
#else
electron::Browser::Get()->SetBadgeCount(value);
#endif
}

void BadgeManager::ClearBadge() {
#if defined(OS_WIN)
electron::Browser::Get()->SetBadgeCount(0, true);
#else
electron::Browser::Get()->SetBadgeCount(0);
#endif
}

} // namespace badging
90 changes: 90 additions & 0 deletions shell/browser/badging/badge_manager.h
@@ -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 changes: 41 additions & 0 deletions shell/browser/badging/badge_manager_factory.cc
@@ -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 changes: 44 additions & 0 deletions shell/browser/badging/badge_manager_factory.h
@@ -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_
2 changes: 2 additions & 0 deletions shell/browser/browser.cc 100644 → 100755
Expand Up @@ -156,9 +156,11 @@ void Browser::SetName(const std::string& name) {
OverrideApplicationName(name);
}

#if !defined(OS_WIN)
int Browser::GetBadgeCount() {
return badge_count_;
}
#endif

bool Browser::OpenFile(const std::string& file_path) {
bool prevent_default = false;
Expand Down
23 changes: 22 additions & 1 deletion shell/browser/browser.h 100644 → 100755
Expand Up @@ -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)
Expand Down Expand Up @@ -107,10 +108,21 @@ class Browser : public WindowListObserver {
#endif

// Set/Get the badge count.
bool SetBadgeCount(int count);
#if defined(OS_WIN)
bool SetBadgeCount(base::Optional<int> count,
base::Optional<bool> shouldSetBadge);
int GetBadgeCount(base::Optional<bool> shouldGetBadge);
#else
bool SetBadgeCount(base::Optional<int> count);
int GetBadgeCount();
#endif

#if defined(OS_WIN)
// Used by WindowsEnumerationBadgeHandler to get the window to set badge for
DWORD badge_process_id;

void UpdateBadgeContents(HWND hwnd);

struct LaunchItem {
base::string16 name;
base::string16 path;
Expand Down Expand Up @@ -364,6 +376,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);
};

Expand Down
8 changes: 4 additions & 4 deletions shell/browser/browser_linux.cc
Expand Up @@ -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;
Expand Down

0 comments on commit 26bd5d0

Please sign in to comment.