Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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