fix: navigator.setAppBadge/clearAppBadge from a service worker (#27950)
This commit is contained in:
parent
d92bab0e29
commit
afb7d9f550
7 changed files with 202 additions and 26 deletions
|
@ -47,6 +47,27 @@ void BadgeManager::BindFrameReceiver(
|
||||||
std::move(context));
|
std::move(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BadgeManager::BindServiceWorkerReceiver(
|
||||||
|
content::RenderProcessHost* service_worker_process_host,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
mojo::PendingReceiver<blink::mojom::BadgeService> receiver) {
|
||||||
|
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||||
|
|
||||||
|
auto* browser_context = service_worker_process_host->GetBrowserContext();
|
||||||
|
|
||||||
|
auto* badge_manager =
|
||||||
|
badging::BadgeManagerFactory::GetInstance()->GetForBrowserContext(
|
||||||
|
browser_context);
|
||||||
|
if (!badge_manager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto context = std::make_unique<BadgeManager::ServiceWorkerBindingContext>(
|
||||||
|
service_worker_process_host->GetID(), service_worker_scope);
|
||||||
|
|
||||||
|
badge_manager->receivers_.Add(badge_manager, std::move(receiver),
|
||||||
|
std::move(context));
|
||||||
|
}
|
||||||
|
|
||||||
std::string BadgeManager::GetBadgeString(base::Optional<int> badge_content) {
|
std::string BadgeManager::GetBadgeString(base::Optional<int> badge_content) {
|
||||||
if (!badge_content)
|
if (!badge_content)
|
||||||
return "•";
|
return "•";
|
||||||
|
|
|
@ -37,6 +37,10 @@ class BadgeManager : public KeyedService, public blink::mojom::BadgeService {
|
||||||
static void BindFrameReceiver(
|
static void BindFrameReceiver(
|
||||||
content::RenderFrameHost* frame,
|
content::RenderFrameHost* frame,
|
||||||
mojo::PendingReceiver<blink::mojom::BadgeService> receiver);
|
mojo::PendingReceiver<blink::mojom::BadgeService> receiver);
|
||||||
|
static void BindServiceWorkerReceiver(
|
||||||
|
content::RenderProcessHost* service_worker_process_host,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
mojo::PendingReceiver<blink::mojom::BadgeService> receiver);
|
||||||
|
|
||||||
// Determines the text to put on the badge based on some badge_content.
|
// Determines the text to put on the badge based on some badge_content.
|
||||||
static std::string GetBadgeString(base::Optional<int> badge_content);
|
static std::string GetBadgeString(base::Optional<int> badge_content);
|
||||||
|
@ -66,6 +70,21 @@ class BadgeManager : public KeyedService, public blink::mojom::BadgeService {
|
||||||
int frame_id_;
|
int frame_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The BindingContext for ServiceWorkerGlobalScope execution contexts.
|
||||||
|
class ServiceWorkerBindingContext final : public BindingContext {
|
||||||
|
public:
|
||||||
|
ServiceWorkerBindingContext(int process_id, const GURL& scope)
|
||||||
|
: process_id_(process_id), scope_(scope) {}
|
||||||
|
~ServiceWorkerBindingContext() override = default;
|
||||||
|
|
||||||
|
int GetProcessId() { return process_id_; }
|
||||||
|
GURL GetScope() { return scope_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int process_id_;
|
||||||
|
GURL scope_;
|
||||||
|
};
|
||||||
|
|
||||||
// blink::mojom::BadgeService:
|
// blink::mojom::BadgeService:
|
||||||
// Note: These are private to stop them being called outside of mojo as they
|
// Note: These are private to stop them being called outside of mojo as they
|
||||||
// require a mojo binding context.
|
// require a mojo binding context.
|
||||||
|
|
|
@ -1776,4 +1776,12 @@ content::BluetoothDelegate* ElectronBrowserClient::GetBluetoothDelegate() {
|
||||||
return bluetooth_delegate_.get();
|
return bluetooth_delegate_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ElectronBrowserClient::BindBadgeServiceReceiverFromServiceWorker(
|
||||||
|
content::RenderProcessHost* service_worker_process_host,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
mojo::PendingReceiver<blink::mojom::BadgeService> receiver) {
|
||||||
|
badging::BadgeManager::BindServiceWorkerReceiver(
|
||||||
|
service_worker_process_host, service_worker_scope, std::move(receiver));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|
|
@ -73,6 +73,10 @@ class ElectronBrowserClient : public content::ContentBrowserClient,
|
||||||
void RegisterBrowserInterfaceBindersForFrame(
|
void RegisterBrowserInterfaceBindersForFrame(
|
||||||
content::RenderFrameHost* render_frame_host,
|
content::RenderFrameHost* render_frame_host,
|
||||||
mojo::BinderMapWithContext<content::RenderFrameHost*>* map) override;
|
mojo::BinderMapWithContext<content::RenderFrameHost*>* map) override;
|
||||||
|
void BindBadgeServiceReceiverFromServiceWorker(
|
||||||
|
content::RenderProcessHost* service_worker_process_host,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
mojo::PendingReceiver<blink::mojom::BadgeService> receiver) override;
|
||||||
#if defined(OS_LINUX)
|
#if defined(OS_LINUX)
|
||||||
void GetAdditionalMappedFilesForChildProcess(
|
void GetAdditionalMappedFilesForChildProcess(
|
||||||
const base::CommandLine& command_line,
|
const base::CommandLine& command_line,
|
||||||
|
|
|
@ -1578,12 +1578,6 @@ describe('navigator.clipboard', () => {
|
||||||
|
|
||||||
ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.setAppBadge/clearAppBadge', () => {
|
ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.setAppBadge/clearAppBadge', () => {
|
||||||
let w: BrowserWindow;
|
let w: BrowserWindow;
|
||||||
before(async () => {
|
|
||||||
w = new BrowserWindow({
|
|
||||||
show: false
|
|
||||||
});
|
|
||||||
await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html'));
|
|
||||||
});
|
|
||||||
|
|
||||||
const expectedBadgeCount = 42;
|
const expectedBadgeCount = 42;
|
||||||
|
|
||||||
|
@ -1603,30 +1597,96 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se
|
||||||
return badgeCount;
|
return badgeCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
after(() => {
|
describe('in the renderer', () => {
|
||||||
app.badgeCount = 0;
|
before(async () => {
|
||||||
closeAllWindows();
|
w = new BrowserWindow({
|
||||||
|
show: false
|
||||||
|
});
|
||||||
|
await w.loadFile(path.join(fixturesPath, 'pages', 'blank.html'));
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('setAppBadge can set a numerical value', async () => {
|
describe('in a service worker', () => {
|
||||||
const result = await fireAppBadgeAction('set', expectedBadgeCount);
|
beforeEach(async () => {
|
||||||
expect(result).to.equal('success');
|
w = new BrowserWindow({
|
||||||
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
|
show: false,
|
||||||
});
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
partition: 'sw-file-scheme-spec',
|
||||||
|
contextIsolation: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('setAppBadge can set an empty(dot) value', async () => {
|
afterEach(() => {
|
||||||
const result = await fireAppBadgeAction('set');
|
app.badgeCount = 0;
|
||||||
expect(result).to.equal('success');
|
closeAllWindows();
|
||||||
expect(waitForBadgeCount(0)).to.eventually.equal(0);
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('clearAppBadge can clear a value', async () => {
|
it('setAppBadge can be called in a ServiceWorker', (done) => {
|
||||||
let result = await fireAppBadgeAction('set', expectedBadgeCount);
|
w.webContents.on('ipc-message', (event, channel, message) => {
|
||||||
expect(result).to.equal('success');
|
if (channel === 'reload') {
|
||||||
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
|
w.webContents.reload();
|
||||||
result = await fireAppBadgeAction('clear');
|
} else if (channel === 'error') {
|
||||||
expect(result).to.equal('success');
|
done(message);
|
||||||
expect(waitForBadgeCount(0)).to.eventually.equal(0);
|
} else if (channel === 'response') {
|
||||||
|
expect(message).to.equal('SUCCESS setting app badge');
|
||||||
|
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
|
||||||
|
session.fromPartition('sw-file-scheme-spec').clearStorageData({
|
||||||
|
storages: ['serviceworkers']
|
||||||
|
}).then(() => done());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
w.webContents.on('crashed', () => done(new Error('WebContents crashed.')));
|
||||||
|
w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'badge-index.html'), { search: '?setBadge' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clearAppBadge can be called in a ServiceWorker', (done) => {
|
||||||
|
w.webContents.on('ipc-message', (event, channel, message) => {
|
||||||
|
if (channel === 'reload') {
|
||||||
|
w.webContents.reload();
|
||||||
|
} else if (channel === 'setAppBadge') {
|
||||||
|
expect(message).to.equal('SUCCESS setting app badge');
|
||||||
|
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
|
||||||
|
} else if (channel === 'error') {
|
||||||
|
done(message);
|
||||||
|
} else if (channel === 'response') {
|
||||||
|
expect(message).to.equal('SUCCESS clearing app badge');
|
||||||
|
expect(waitForBadgeCount(expectedBadgeCount)).to.eventually.equal(expectedBadgeCount);
|
||||||
|
session.fromPartition('sw-file-scheme-spec').clearStorageData({
|
||||||
|
storages: ['serviceworkers']
|
||||||
|
}).then(() => done());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
w.webContents.on('crashed', () => done(new Error('WebContents crashed.')));
|
||||||
|
w.loadFile(path.join(fixturesPath, 'pages', 'service-worker', 'badge-index.html'), { search: '?clearBadge' });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
31
spec/fixtures/pages/service-worker/badge-index.html
vendored
Normal file
31
spec/fixtures/pages/service-worker/badge-index.html
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<script>
|
||||||
|
const ipcRenderer = require('electron').ipcRenderer;
|
||||||
|
let search = (new URL(document.location)).search;
|
||||||
|
|
||||||
|
async function testIt() {
|
||||||
|
if (search === '?clearBadge') {
|
||||||
|
try {
|
||||||
|
await navigator.setAppBadge(42);
|
||||||
|
ipcRenderer.send('setAppBadge','SUCCESS setting app badge');
|
||||||
|
} catch (error) {
|
||||||
|
ipcRenderer.send('error', `${error.message}\n${error.stack}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
navigator.serviceWorker.register('service-worker-badge.js', {scope: './'}).then(function() {
|
||||||
|
if (navigator.serviceWorker.controller) {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', 'http://dummy/echo'+search);
|
||||||
|
xhr.setRequestHeader('X-Mock-Response', 'yes');
|
||||||
|
xhr.addEventListener('load', function() {
|
||||||
|
ipcRenderer.send('response', xhr.responseText);
|
||||||
|
});
|
||||||
|
xhr.send();
|
||||||
|
} else {
|
||||||
|
ipcRenderer.send('reload');
|
||||||
|
}
|
||||||
|
}).catch(function(error) {
|
||||||
|
ipcRenderer.send('error', `${error.message}\n${error.stack}`);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
testIt();
|
||||||
|
</script>
|
33
spec/fixtures/pages/service-worker/service-worker-badge.js
vendored
Normal file
33
spec/fixtures/pages/service-worker/service-worker-badge.js
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
self.addEventListener('fetch', async function (event) {
|
||||||
|
const requestUrl = new URL(event.request.url);
|
||||||
|
let responseTxt;
|
||||||
|
if (requestUrl.pathname === '/echo' &&
|
||||||
|
event.request.headers.has('X-Mock-Response')) {
|
||||||
|
if (requestUrl.search === '?setBadge') {
|
||||||
|
if (navigator.setAppBadge()) {
|
||||||
|
try {
|
||||||
|
await navigator.setAppBadge(42);
|
||||||
|
responseTxt = 'SUCCESS setting app badge';
|
||||||
|
await navigator.clearAppBadge();
|
||||||
|
} catch (ex) {
|
||||||
|
responseTxt = 'ERROR setting app badge ' + ex;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
responseTxt = 'ERROR navigator.setAppBadge is not available in ServiceWorker!';
|
||||||
|
}
|
||||||
|
} else if (requestUrl.search === '?clearBadge') {
|
||||||
|
if (navigator.clearAppBadge()) {
|
||||||
|
try {
|
||||||
|
await navigator.clearAppBadge();
|
||||||
|
responseTxt = 'SUCCESS clearing app badge';
|
||||||
|
} catch (ex) {
|
||||||
|
responseTxt = 'ERROR clearing app badge ' + ex;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
responseTxt = 'ERROR navigator.clearAppBadge is not available in ServiceWorker!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const mockResponse = new Response(responseTxt);
|
||||||
|
event.respondWith(mockResponse);
|
||||||
|
}
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue