| File: Navigator.cs | Web Access |
| Project: Microsoft.AspNetCore.Components.Platform.csproj (Microsoft.AspNetCore.Components.Platform) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; using System.Runtime.InteropServices; using Microsoft.JSInterop; namespace Microsoft.AspNetCore.Components.Platform; /// <summary> /// Represents the state and identity of the browser. /// </summary> public sealed class Navigator : IInternalAsyncDisposal { private readonly IJSRuntime _jsRuntime; private readonly Window _window; private Task<IJSObjectReference>? _referenceTask; private bool _disposed; internal Navigator(IJSRuntime jsRuntime, Window window) { _jsRuntime = jsRuntime; _window = window; } /// <summary> /// Gets the preferred language of the user, usually the language of the browser UI. /// </summary> /// <returns>The language tag, such as <c>"en-US"</c>.</returns> public async ValueTask<string> GetLanguageAsync() { var navigator = await GetReferenceAsync().ConfigureAwait(false); return await navigator.GetValueAsync<string>("language").ConfigureAwait(false); } /// <summary> /// Gets the languages that the user prefers, with the most preferred language first. /// </summary> /// <returns>The language tags, such as <c>"en-US"</c>.</returns> public async ValueTask<ImmutableArray<string>> GetLanguagesAsync() { var navigator = await GetReferenceAsync().ConfigureAwait(false); var languages = await navigator.GetValueAsync<string[]>("languages").ConfigureAwait(false); return ImmutableCollectionsMarshal.AsImmutableArray(languages); } /// <summary> /// Gets a value indicating whether the browser is online. /// </summary> /// <remarks> /// A value of <see langword="true"/> does not mean that the internet can be reached. /// </remarks> /// <returns><see langword="true"/> if the browser is online; otherwise, <see langword="false"/>.</returns> public async ValueTask<bool> GetOnLineAsync() { var navigator = await GetReferenceAsync().ConfigureAwait(false); return await navigator.GetValueAsync<bool>("onLine").ConfigureAwait(false); } /// <summary> /// Gets a value indicating whether the browser accepts cookies. /// </summary> /// <returns><see langword="true"/> if the browser accepts cookies; otherwise, <see langword="false"/>.</returns> public async ValueTask<bool> GetCookieEnabledAsync() { var navigator = await GetReferenceAsync().ConfigureAwait(false); return await navigator.GetValueAsync<bool>("cookieEnabled").ConfigureAwait(false); } /// <summary> /// Gets the user agent string of the browser. /// </summary> /// <returns>The user agent string.</returns> public async ValueTask<string> GetUserAgentAsync() { var navigator = await GetReferenceAsync().ConfigureAwait(false); return await navigator.GetValueAsync<string>("userAgent").ConfigureAwait(false); } /// <summary> /// Gets the number of logical processors that the browser can use to run threads. /// </summary> /// <returns>The number of logical processors.</returns> public async ValueTask<ulong> GetHardwareConcurrencyAsync() { var navigator = await GetReferenceAsync().ConfigureAwait(false); return await navigator.GetValueAsync<ulong>("hardwareConcurrency").ConfigureAwait(false); } /// <summary> /// Gets the maximum number of simultaneous touch points that the device supports. /// </summary> /// <returns>The maximum number of touch points, or 0 if the device does not support touch input.</returns> public async ValueTask<int> GetMaxTouchPointsAsync() { var navigator = await GetReferenceAsync().ConfigureAwait(false); return await navigator.GetValueAsync<int>("maxTouchPoints").ConfigureAwait(false); } /// <summary> /// Gets the projection of type <typeparamref name="T"/> that hangs off the navigator, creating it on first access. /// </summary> /// <typeparam name="T">The projection type.</typeparam> /// <param name="factory">The callback that creates the projection.</param> /// <returns>The projection keyed by <typeparamref name="T"/>.</returns> internal T GetOrAddFeature<T>(Func<FeatureContext, T> factory) where T : class { ArgumentNullException.ThrowIfNull(factory); // Window keeps every projection of the scope, so its disposal reaches this one too. return _window.GetOrAddFeature(factory); } async ValueTask IInternalAsyncDisposal.InternalDisposeAsync() { if (_disposed) { return; } _disposed = true; // Don't wait for a pending acquisition. It could hold up scope disposal until the JS interop timeout. if (_referenceTask is { IsCompletedSuccessfully: true } referenceTask) { await referenceTask.Result.DisposeAsync().ConfigureAwait(false); } } private Task<IJSObjectReference> GetReferenceAsync() { ObjectDisposedException.ThrowIf(_disposed, this); if (_referenceTask is null || _referenceTask.IsFaulted || _referenceTask.IsCanceled) { _referenceTask = _jsRuntime.GetValueAsync<IJSObjectReference>("navigator").AsTask(); } return _referenceTask; } }