Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/vs/platform/meteredConnection/common/meteredConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export interface IMeteredConnectionService {
*/
readonly isConnectionMetered: boolean;

/**
* Resolves once the initial connection state is available, when initialization is asynchronous.
*/
readonly whenConnectionStateInitialized?: Promise<void>;

/**
* Event that fires when the metered connection status changes.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
import { toDisposable } from '../../../base/common/lifecycle.js';
import { IChannel } from '../../../base/parts/ipc/common/ipc.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';
import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js';
import { SyncDescriptor } from '../../instantiation/common/descriptors.js';
import { registerSingleton } from '../../instantiation/common/extensions.js';
import { IMainProcessService } from '../../ipc/common/mainProcessService.js';
import { AbstractMeteredConnectionService, getIsBrowserConnectionMetered, IMeteredConnectionService, NavigatorWithConnection } from '../common/meteredConnection.js';
import { METERED_CONNECTION_CHANNEL, MeteredConnectionCommand } from '../common/meteredConnectionIpc.js';
Expand All @@ -19,15 +20,17 @@ export class NativeMeteredConnectionService extends AbstractMeteredConnectionSer
private readonly _channel: IChannel;

constructor(
private readonly connectionMeteredDetector: () => boolean,
@IConfigurationService configurationService: IConfigurationService,
@IMainProcessService mainProcessService: IMainProcessService
) {
super(configurationService, getIsBrowserConnectionMetered());
super(configurationService, connectionMeteredDetector());
this._channel = mainProcessService.getChannel(METERED_CONNECTION_CHANNEL);
void this._channel.call(MeteredConnectionCommand.SetIsBrowserConnectionMetered, this.isBrowserConnectionMetered);
Comment thread
dmitrivMS marked this conversation as resolved.

const connection = (navigator as NavigatorWithConnection).connection;
if (connection) {
const onChange = () => this.setIsBrowserConnectionMetered(getIsBrowserConnectionMetered());
const onChange = () => this.setIsBrowserConnectionMetered(this.connectionMeteredDetector());
connection.addEventListener('change', onChange);
this._register(toDisposable(() => connection.removeEventListener('change', onChange)));
}
Expand All @@ -42,4 +45,4 @@ export class NativeMeteredConnectionService extends AbstractMeteredConnectionSer
}
}

registerSingleton(IMeteredConnectionService, NativeMeteredConnectionService, InstantiationType.Delayed);
registerSingleton(IMeteredConnectionService, new SyncDescriptor(NativeMeteredConnectionService, [getIsBrowserConnectionMetered], false));
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { DeferredPromise } from '../../../base/common/async.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
import { AbstractMeteredConnectionService } from '../common/meteredConnection.js';
Expand All @@ -13,6 +14,8 @@ import { AbstractMeteredConnectionService } from '../common/meteredConnection.js
*/
export class MeteredConnectionMainService extends AbstractMeteredConnectionService {
private telemetryService: ITelemetryService | undefined;
private readonly connectionStateInitialized = new DeferredPromise<void>();
readonly whenConnectionStateInitialized = this.connectionStateInitialized.p;

constructor(@IConfigurationService configurationService: IConfigurationService) {
super(configurationService, false);
Expand All @@ -22,6 +25,11 @@ export class MeteredConnectionMainService extends AbstractMeteredConnectionServi
this.telemetryService = telemetryService;
}

public override setIsBrowserConnectionMetered(value: boolean): void {
super.setIsBrowserConnectionMetered(value);
this.connectionStateInitialized.complete();
}

protected override onChangeBrowserConnection() {
// Fire event after sending telemetry if switching to metered since telemetry will be paused.
const fireAfter = this.isBrowserConnectionMetered;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import assert from 'assert';
import { CancellationToken } from '../../../../base/common/cancellation.js';
import { Event } from '../../../../base/common/event.js';
import { IChannel } from '../../../../base/parts/ipc/common/ipc.js';
import { mock } from '../../../../base/test/common/mock.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
import { TestConfigurationService } from '../../../configuration/test/common/testConfigurationService.js';
import { IMainProcessService } from '../../../ipc/common/mainProcessService.js';
import { METERED_CONNECTION_CHANNEL, MeteredConnectionCommand } from '../../common/meteredConnectionIpc.js';
import { NativeMeteredConnectionService } from '../../electron-browser/meteredConnectionService.js';

class TestChannel implements IChannel {
readonly calls: { command: string; argument: unknown }[] = [];

call<T>(command: string, arg?: unknown, _cancellationToken?: CancellationToken): Promise<T> {
this.calls.push({ command, argument: arg });
return Promise.resolve(undefined as T);
}

listen<T>(_event: string, _arg?: unknown): Event<T> {
return Event.None;
}
}

suite('NativeMeteredConnectionService', () => {
const store = ensureNoDisposablesAreLeakedInTestSuite();

test('reports the initial browser connection state to the main process', () => {
const channel = new TestChannel();
const mainProcessService = new class extends mock<IMainProcessService>() {
override getChannel(channelName: string): IChannel {
assert.strictEqual(channelName, METERED_CONNECTION_CHANNEL);
return channel;
}
};
const configurationService = new TestConfigurationService();
store.add(configurationService.onDidChangeConfigurationEmitter);

store.add(new NativeMeteredConnectionService(() => true, configurationService, mainProcessService));

assert.deepStrictEqual(channel.calls, [{
command: MeteredConnectionCommand.SetIsBrowserConnectionMetered,
argument: true,
}]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import assert from 'assert';
import { timeout } from '../../../../base/common/async.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
import { TestConfigurationService } from '../../../configuration/test/common/testConfigurationService.js';
import { MeteredConnectionMainService } from '../../electron-main/meteredConnectionMainService.js';

suite('MeteredConnectionMainService', () => {
const store = ensureNoDisposablesAreLeakedInTestSuite();

test('initialization waits for the initial browser connection state', async () => {
const configurationService = new TestConfigurationService();
store.add(configurationService.onDidChangeConfigurationEmitter);
const service = store.add(new MeteredConnectionMainService(configurationService));
let initialized = false;
void service.whenConnectionStateInitialized.then(() => initialized = true);

await timeout(0);
assert.strictEqual(initialized, false);

service.setIsBrowserConnectionMetered(true);
await service.whenConnectionStateInitialized;

assert.deepStrictEqual({
initialized,
isConnectionMetered: service.isConnectionMetered,
}, {
initialized: true,
isConnectionMetered: true,
});
});
});
Loading
Loading