From d3f6b11d2ff1be0308de77c60549adf566dec6a9 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Fri, 20 Aug 2021 03:34:40 -0700 Subject: [PATCH 1/6] React to SDK, ASP.NET Core updates --- .../MauiRazorClassLibrarySample.csproj | 1 - .../WebViewAppShared/WebViewAppShared.csproj | 1 - ...AspNetCore.Components.WebView.Maui.targets | 17 - .../ManifestStaticWebAssetsFileProvider.cs | 391 ----------- .../Internal/StaticContentProvider.cs | 608 ------------------ .../src/WebView2/WebView2WebViewManager.cs | 9 +- 6 files changed, 1 insertion(+), 1026 deletions(-) delete mode 100644 src/BlazorWebView/src/WebView2/Internal/ManifestStaticWebAssetsFileProvider.cs delete mode 100644 src/BlazorWebView/src/WebView2/Internal/StaticContentProvider.cs diff --git a/src/BlazorWebView/samples/MauiRazorClassLibrarySample/MauiRazorClassLibrarySample.csproj b/src/BlazorWebView/samples/MauiRazorClassLibrarySample/MauiRazorClassLibrarySample.csproj index d4e53c2f4e87..dd59f227b2e4 100644 --- a/src/BlazorWebView/samples/MauiRazorClassLibrarySample/MauiRazorClassLibrarySample.csproj +++ b/src/BlazorWebView/samples/MauiRazorClassLibrarySample/MauiRazorClassLibrarySample.csproj @@ -3,7 +3,6 @@ net6.0 false - false diff --git a/src/BlazorWebView/samples/WebViewAppShared/WebViewAppShared.csproj b/src/BlazorWebView/samples/WebViewAppShared/WebViewAppShared.csproj index b61a5dad0b2b..f74cf97210f3 100644 --- a/src/BlazorWebView/samples/WebViewAppShared/WebViewAppShared.csproj +++ b/src/BlazorWebView/samples/WebViewAppShared/WebViewAppShared.csproj @@ -3,7 +3,6 @@ net6.0 false - false diff --git a/src/BlazorWebView/src/Maui/build/Microsoft.AspNetCore.Components.WebView.Maui.targets b/src/BlazorWebView/src/Maui/build/Microsoft.AspNetCore.Components.WebView.Maui.targets index dee497a8e8fd..e0e19e95e30d 100644 --- a/src/BlazorWebView/src/Maui/build/Microsoft.AspNetCore.Components.WebView.Maui.targets +++ b/src/BlazorWebView/src/Maui/build/Microsoft.AspNetCore.Components.WebView.Maui.targets @@ -48,21 +48,4 @@ - - - - - - <_MauiManifestsToUpdate Include="@(StaticWebAssetManifest)" Condition="'%(ManifestType)' == 'Publish'" /> - <_MauiManifestsToUpdate> - @(_MauiManifestsToUpdate->'%(AdditionalPublishPropertiesToRemove)');TargetFramework - - - - - - - diff --git a/src/BlazorWebView/src/WebView2/Internal/ManifestStaticWebAssetsFileProvider.cs b/src/BlazorWebView/src/WebView2/Internal/ManifestStaticWebAssetsFileProvider.cs deleted file mode 100644 index 3ea9efd76b80..000000000000 --- a/src/BlazorWebView/src/WebView2/Internal/ManifestStaticWebAssetsFileProvider.cs +++ /dev/null @@ -1,391 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Text.Json; -using System.Text.Json.Serialization; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.FileSystemGlobbing; -using Microsoft.Extensions.Primitives; - -namespace Microsoft.AspNetCore.Components.WebView.WebView2.Tmp -{ - internal sealed class ManifestStaticWebAssetFileProvider : IFileProvider - { - private static readonly StringComparison _fsComparison = OperatingSystem.IsWindows() ? - StringComparison.OrdinalIgnoreCase : - StringComparison.Ordinal; - - private static readonly IEqualityComparer _nameComparer = new FileNameComparer(); - - private readonly IFileProvider[] _fileProviders; - private readonly StaticWebAssetNode _root; - - public ManifestStaticWebAssetFileProvider(StaticWebAssetManifest manifest, Func fileProviderFactory) - { - _fileProviders = new IFileProvider[manifest.ContentRoots.Length]; - - - for (int i = 0; i < manifest.ContentRoots.Length; i++) - { - _fileProviders[i] = fileProviderFactory(manifest.ContentRoots[i]); - } - - _root = manifest.Root; - } - - public IDirectoryContents GetDirectoryContents(string subpath) - { - if (subpath == null) - { - throw new ArgumentNullException(nameof(subpath)); - } - - var segments = Normalize(subpath).Split('/', StringSplitOptions.RemoveEmptyEntries); - var candidate = _root; - - // Iterate over the path segments until we reach the destination. Whenever we encounter - // a pattern, we start tracking it as well as the content root directory. On every segment - // we evalutate the directory to see if there is a subfolder with the current segment and - // replace it on the dictionary if it exists or remove it if it does not. - // When we reach our destination we enumerate the files in that folder and evalute them against - // the pattern to compute the final list. - HashSet files = null; - for (var i = 0; i < segments.Length; i++) - { - files = GetFilesForCandidatePatterns(segments, candidate, files); - - - if (candidate.HasChildren() && candidate.Children.TryGetValue(segments[i], out var child)) - { - candidate = child; - } - else - { - candidate = null; - break; - } - } - - if ((candidate == null || (!candidate.HasChildren() && !candidate.HasPatterns())) && files == null) - { - return NotFoundDirectoryContents.Singleton; - } - else - { - // We do this to make sure we account for the patterns on the last segment which are not covered by the loop above - files = GetFilesForCandidatePatterns(segments, candidate, files); - if (candidate != null && candidate.HasChildren()) - { - files ??= new(_nameComparer); - GetCandidateFilesForNode(candidate, files); - } - - return new StaticWebAssetsDirectoryContents((files as IEnumerable) ?? Array.Empty()); - } - - HashSet GetFilesForCandidatePatterns(string[] segments, StaticWebAssetNode candidate, HashSet files) - { - if (candidate != null && candidate.HasPatterns()) - { - var depth = candidate.Patterns[0].Depth; - var candidateDirectoryPath = string.Join('/', segments[depth..]); - foreach (var pattern in candidate.Patterns) - { - var contentRoot = _fileProviders[pattern.ContentRoot]; - var matcher = new Matcher(_fsComparison); - matcher.AddInclude(pattern.Pattern); - foreach (var result in contentRoot.GetDirectoryContents(candidateDirectoryPath)) - { - var fileCandidate = string.IsNullOrEmpty(candidateDirectoryPath) ? result.Name : $"{candidateDirectoryPath}/{result.Name}"; - if (result.Exists && (result.IsDirectory || matcher.Match(fileCandidate).HasMatches)) - { - files ??= new(_nameComparer); - if (!files.Contains(result)) - { - // Multiple patterns might match the same file (even at different locations on disk) at runtime. We don't - // try to disambiguate anything here, since there is already a build step for it. We just pick the first - // file that matches the pattern. The manifest entries are ordered, so while this choice is random, it is - // nonetheless deterministic. - files.Add(result); - } - } - } - } - } - - - return files; - } - - void GetCandidateFilesForNode(StaticWebAssetNode candidate, HashSet files) - { - foreach (var child in candidate.Children!) - { - var match = child.Value.Match; - if (match == null) - { - // This is a folder - var file = new StaticWebAssetsDirectoryInfo(child.Key); - // Entries from the manifest always win over any content based on patterns, - // so remove any potentially existing file or folder in favor of the manifest - // entry. - files.Remove(file); - files.Add(file); - } - else - { - // This is a file. - files.RemoveWhere(f => string.Equals(match.Path, f.Name, _fsComparison)); - var file = _fileProviders[match.ContentRoot].GetFileInfo(match.Path); - - - files.Add(string.Equals(child.Key, match.Path, _fsComparison) ? file : - // This means that this file was mapped, there is a chance that we added it to the list - // of files by one of the patterns, so we need to replace it with the mapped file. - new StaticWebAssetsFileInfo(child.Key, file)); - } - } - } - } - - private string Normalize(string path) - { - return path.Replace('\\', '/'); - } - - public IFileInfo GetFileInfo(string subpath) - { - if (subpath == null) - { - throw new ArgumentNullException(nameof(subpath)); - } - - var segments = subpath.Split('/', StringSplitOptions.RemoveEmptyEntries); - StaticWebAssetNode candidate = _root; - List patterns = null; - - // Iterate over the path segments until we reach the destination, collecting - // all pattern candidates along the way except for any pattern at the root. - for (var i = 0; i < segments.Length; i++) - { - if (candidate.HasPatterns()) - { - patterns ??= new(); - patterns.AddRange(candidate.Patterns); - } - if (candidate.HasChildren() && candidate.Children.TryGetValue(segments[i], out var child)) - { - candidate = child; - } - else - { - candidate = null; - break; - } - } - - var match = candidate?.Match; - if (match != null) - { - // If we found a file, that wins over anything else. If there are conflicts with files added after - // we've built the manifest, we'll be notified the next time we do a build. This is not different - // from previous Static Web Assets versions. - var file = _fileProviders[match.ContentRoot].GetFileInfo(match.Path); - if (!file.Exists || string.Equals(subpath, Normalize(match.Path), _fsComparison)) - { - return file; - } - else - { - return new StaticWebAssetsFileInfo(segments[^1], file); - } - } - - // The list of patterns is ordered by pattern depth, so we compute the string to check for patterns only - // once per level. We don't aim to solve conflicts here where multiple files could match a given path, - // we have a build check that takes care of that. - var currentDepth = 0; - var candidatePath = subpath; - - if (patterns != null) - { - for (var i = 0; i < patterns.Count; i++) - { - var pattern = patterns[i]; - if (pattern.Depth != currentDepth) - { - currentDepth = pattern.Depth; - candidatePath = string.Join('/', segments[currentDepth..]); - } - - - var result = _fileProviders[pattern.ContentRoot].GetFileInfo(candidatePath); - if (result.Exists) - { - if (!result.IsDirectory) - { - var matcher = new Matcher(); - matcher.AddInclude(pattern.Pattern); - if (!matcher.Match(candidatePath).HasMatches) - { - continue; - } - - - return result; - } - } - } - } - - return new NotFoundFileInfo(subpath); - } - - public IChangeToken Watch(string filter) => NullChangeToken.Singleton; - - private sealed class StaticWebAssetsDirectoryContents : IDirectoryContents - { - private readonly IEnumerable _files; - - public StaticWebAssetsDirectoryContents(IEnumerable files) => - _files = files; - - public bool Exists => true; - - public IEnumerator GetEnumerator() => _files.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - private sealed class StaticWebAssetsDirectoryInfo : IFileInfo - { - private static readonly DateTimeOffset _lastModified = DateTimeOffset.FromUnixTimeSeconds(0); - - - public StaticWebAssetsDirectoryInfo(string name) - { - Name = name; - } - - - public bool Exists => true; - - public long Length => 0; - - public string PhysicalPath => null; - - public DateTimeOffset LastModified => _lastModified; - - public bool IsDirectory => true; - - public string Name { get; } - - public Stream CreateReadStream() => throw new InvalidOperationException("Can not create a stream for a directory."); - } - - private sealed class StaticWebAssetsFileInfo : IFileInfo - { - private readonly IFileInfo _source; - - public StaticWebAssetsFileInfo(string name, IFileInfo source) - { - Name = name; - _source = source; - } - - public bool Exists => _source.Exists; - - public long Length => _source.Length; - - public string PhysicalPath => _source.PhysicalPath; - - public DateTimeOffset LastModified => _source.LastModified; - - public bool IsDirectory => _source.IsDirectory; - - public string Name { get; } - - public Stream CreateReadStream() => _source.CreateReadStream(); - } - - - private sealed class FileNameComparer : IEqualityComparer - { - public bool Equals(IFileInfo x, IFileInfo y) => string.Equals(x?.Name, y?.Name, _fsComparison); - - public int GetHashCode(IFileInfo obj) => obj.Name.GetHashCode(_fsComparison); - } - - - internal sealed class StaticWebAssetManifest - { - internal static readonly StringComparer PathComparer = - OperatingSystem.IsWindows() ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal; - - public string[] ContentRoots { get; set; } = Array.Empty(); - - public StaticWebAssetNode Root { get; set; } = null!; - - internal static StaticWebAssetManifest Parse(Stream manifest) - { - return JsonSerializer.Deserialize(manifest)!; - } - } - - internal sealed class StaticWebAssetNode - { - [JsonPropertyName("Asset")] - public StaticWebAssetMatch Match { get; set; } - - [JsonConverter(typeof(OSBasedCaseConverter))] - public Dictionary Children { get; set; } - - public StaticWebAssetPattern[] Patterns { get; set; } - - [MemberNotNullWhen(true, nameof(Children))] - internal bool HasChildren() => Children != null && Children.Count > 0; - - [MemberNotNullWhen(true, nameof(Patterns))] - internal bool HasPatterns() => Patterns != null && Patterns.Length > 0; - } - - internal sealed class StaticWebAssetMatch - { - [JsonPropertyName("ContentRootIndex")] - public int ContentRoot { get; set; } - - [JsonPropertyName("SubPath")] - public string Path { get; set; } = null!; - } - - internal sealed class StaticWebAssetPattern - { - [JsonPropertyName("ContentRootIndex")] - public int ContentRoot { get; set; } - - public int Depth { get; set; } - - public string Pattern { get; set; } = null!; - } - - private sealed class OSBasedCaseConverter : JsonConverter> - { - public override Dictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new Dictionary( - JsonSerializer.Deserialize>(ref reader, options)!, - StaticWebAssetManifest.PathComparer); - } - - public override void Write(Utf8JsonWriter writer, Dictionary value, JsonSerializerOptions options) - { - JsonSerializer.Serialize(writer, value, options); - } - } - } -} diff --git a/src/BlazorWebView/src/WebView2/Internal/StaticContentProvider.cs b/src/BlazorWebView/src/WebView2/Internal/StaticContentProvider.cs deleted file mode 100644 index cc4f6d530782..000000000000 --- a/src/BlazorWebView/src/WebView2/Internal/StaticContentProvider.cs +++ /dev/null @@ -1,608 +0,0 @@ -#nullable disable -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -#if !MAUI_INCLUDE -using Microsoft.AspNetCore.Components.WebView.WebView2.Tmp; -#endif -using Microsoft.Extensions.FileProviders; - -namespace Microsoft.AspNetCore.Components.WebView.WebView2.Internal -{ - internal partial class StaticContentProvider - { - private static readonly FileExtensionContentTypeProvider ContentTypeProvider = new(); - - internal static string GetResponseContentTypeOrDefault(string path) - => ContentTypeProvider.TryGetContentType(path, out var matchedContentType) - ? matchedContentType - : "application/octet-stream"; - - internal static IDictionary GetResponseHeaders(string contentType) - => new Dictionary() - { - { "Content-Type", contentType }, - { "Cache-Control", "no-cache, max-age=0, must-revalidate, no-store" }, - }; - - internal class FileExtensionContentTypeProvider - { - // Notes: - // - This table was initially copied from IIS and has many legacy entries we will maintain for backwards compatibility. - // - We only plan to add new entries where we expect them to be applicable to a majority of developers such as being - // used in the project templates. - #region Extension mapping table - /// - /// Creates a new provider with a set of default mappings. - /// - public FileExtensionContentTypeProvider() - : this(new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { ".323", "text/h323" }, - { ".3g2", "video/3gpp2" }, - { ".3gp2", "video/3gpp2" }, - { ".3gp", "video/3gpp" }, - { ".3gpp", "video/3gpp" }, - { ".aac", "audio/aac" }, - { ".aaf", "application/octet-stream" }, - { ".aca", "application/octet-stream" }, - { ".accdb", "application/msaccess" }, - { ".accde", "application/msaccess" }, - { ".accdt", "application/msaccess" }, - { ".acx", "application/internet-property-stream" }, - { ".adt", "audio/vnd.dlna.adts" }, - { ".adts", "audio/vnd.dlna.adts" }, - { ".afm", "application/octet-stream" }, - { ".ai", "application/postscript" }, - { ".aif", "audio/x-aiff" }, - { ".aifc", "audio/aiff" }, - { ".aiff", "audio/aiff" }, - { ".appcache", "text/cache-manifest" }, - { ".application", "application/x-ms-application" }, - { ".art", "image/x-jg" }, - { ".asd", "application/octet-stream" }, - { ".asf", "video/x-ms-asf" }, - { ".asi", "application/octet-stream" }, - { ".asm", "text/plain" }, - { ".asr", "video/x-ms-asf" }, - { ".asx", "video/x-ms-asf" }, - { ".atom", "application/atom+xml" }, - { ".au", "audio/basic" }, - { ".avi", "video/x-msvideo" }, - { ".axs", "application/olescript" }, - { ".bas", "text/plain" }, - { ".bcpio", "application/x-bcpio" }, - { ".bin", "application/octet-stream" }, - { ".bmp", "image/bmp" }, - { ".c", "text/plain" }, - { ".cab", "application/vnd.ms-cab-compressed" }, - { ".calx", "application/vnd.ms-office.calx" }, - { ".cat", "application/vnd.ms-pki.seccat" }, - { ".cdf", "application/x-cdf" }, - { ".chm", "application/octet-stream" }, - { ".class", "application/x-java-applet" }, - { ".clp", "application/x-msclip" }, - { ".cmx", "image/x-cmx" }, - { ".cnf", "text/plain" }, - { ".cod", "image/cis-cod" }, - { ".cpio", "application/x-cpio" }, - { ".cpp", "text/plain" }, - { ".crd", "application/x-mscardfile" }, - { ".crl", "application/pkix-crl" }, - { ".crt", "application/x-x509-ca-cert" }, - { ".csh", "application/x-csh" }, - { ".css", "text/css" }, - { ".csv", "text/csv" }, // https://tools.ietf.org/html/rfc7111#section-5.1 - { ".cur", "application/octet-stream" }, - { ".dcr", "application/x-director" }, - { ".deploy", "application/octet-stream" }, - { ".der", "application/x-x509-ca-cert" }, - { ".dib", "image/bmp" }, - { ".dir", "application/x-director" }, - { ".disco", "text/xml" }, - { ".dlm", "text/dlm" }, - { ".doc", "application/msword" }, - { ".docm", "application/vnd.ms-word.document.macroEnabled.12" }, - { ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }, - { ".dot", "application/msword" }, - { ".dotm", "application/vnd.ms-word.template.macroEnabled.12" }, - { ".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template" }, - { ".dsp", "application/octet-stream" }, - { ".dtd", "text/xml" }, - { ".dvi", "application/x-dvi" }, - { ".dvr-ms", "video/x-ms-dvr" }, - { ".dwf", "drawing/x-dwf" }, - { ".dwp", "application/octet-stream" }, - { ".dxr", "application/x-director" }, - { ".eml", "message/rfc822" }, - { ".emz", "application/octet-stream" }, - { ".eot", "application/vnd.ms-fontobject" }, - { ".eps", "application/postscript" }, - { ".etx", "text/x-setext" }, - { ".evy", "application/envoy" }, - { ".exe", "application/vnd.microsoft.portable-executable" }, // https://www.iana.org/assignments/media-types/application/vnd.microsoft.portable-executable - { ".fdf", "application/vnd.fdf" }, - { ".fif", "application/fractals" }, - { ".fla", "application/octet-stream" }, - { ".flr", "x-world/x-vrml" }, - { ".flv", "video/x-flv" }, - { ".gif", "image/gif" }, - { ".gtar", "application/x-gtar" }, - { ".gz", "application/x-gzip" }, - { ".h", "text/plain" }, - { ".hdf", "application/x-hdf" }, - { ".hdml", "text/x-hdml" }, - { ".hhc", "application/x-oleobject" }, - { ".hhk", "application/octet-stream" }, - { ".hhp", "application/octet-stream" }, - { ".hlp", "application/winhlp" }, - { ".hqx", "application/mac-binhex40" }, - { ".hta", "application/hta" }, - { ".htc", "text/x-component" }, - { ".htm", "text/html" }, - { ".html", "text/html" }, - { ".htt", "text/webviewhtml" }, - { ".hxt", "text/html" }, - { ".ical", "text/calendar" }, - { ".icalendar", "text/calendar" }, - { ".ico", "image/x-icon" }, - { ".ics", "text/calendar" }, - { ".ief", "image/ief" }, - { ".ifb", "text/calendar" }, - { ".iii", "application/x-iphone" }, - { ".inf", "application/octet-stream" }, - { ".ins", "application/x-internet-signup" }, - { ".isp", "application/x-internet-signup" }, - { ".IVF", "video/x-ivf" }, - { ".jar", "application/java-archive" }, - { ".java", "application/octet-stream" }, - { ".jck", "application/liquidmotion" }, - { ".jcz", "application/liquidmotion" }, - { ".jfif", "image/pjpeg" }, - { ".jpb", "application/octet-stream" }, - { ".jpe", "image/jpeg" }, - { ".jpeg", "image/jpeg" }, - { ".jpg", "image/jpeg" }, - { ".js", "application/javascript" }, - { ".json", "application/json" }, - { ".jsx", "text/jscript" }, - { ".latex", "application/x-latex" }, - { ".lit", "application/x-ms-reader" }, - { ".lpk", "application/octet-stream" }, - { ".lsf", "video/x-la-asf" }, - { ".lsx", "video/x-la-asf" }, - { ".lzh", "application/octet-stream" }, - { ".m13", "application/x-msmediaview" }, - { ".m14", "application/x-msmediaview" }, - { ".m1v", "video/mpeg" }, - { ".m2ts", "video/vnd.dlna.mpeg-tts" }, - { ".m3u", "audio/x-mpegurl" }, - { ".m4a", "audio/mp4" }, - { ".m4v", "video/mp4" }, - { ".man", "application/x-troff-man" }, - { ".manifest", "application/x-ms-manifest" }, - { ".map", "text/plain" }, - { ".markdown", "text/markdown" }, - { ".md", "text/markdown" }, - { ".mdb", "application/x-msaccess" }, - { ".mdp", "application/octet-stream" }, - { ".me", "application/x-troff-me" }, - { ".mht", "message/rfc822" }, - { ".mhtml", "message/rfc822" }, - { ".mid", "audio/mid" }, - { ".midi", "audio/mid" }, - { ".mix", "application/octet-stream" }, - { ".mmf", "application/x-smaf" }, - { ".mno", "text/xml" }, - { ".mny", "application/x-msmoney" }, - { ".mov", "video/quicktime" }, - { ".movie", "video/x-sgi-movie" }, - { ".mp2", "video/mpeg" }, - { ".mp3", "audio/mpeg" }, - { ".mp4", "video/mp4" }, - { ".mp4v", "video/mp4" }, - { ".mpa", "video/mpeg" }, - { ".mpe", "video/mpeg" }, - { ".mpeg", "video/mpeg" }, - { ".mpg", "video/mpeg" }, - { ".mpp", "application/vnd.ms-project" }, - { ".mpv2", "video/mpeg" }, - { ".ms", "application/x-troff-ms" }, - { ".msi", "application/octet-stream" }, - { ".mso", "application/octet-stream" }, - { ".mvb", "application/x-msmediaview" }, - { ".mvc", "application/x-miva-compiled" }, - { ".nc", "application/x-netcdf" }, - { ".nsc", "video/x-ms-asf" }, - { ".nws", "message/rfc822" }, - { ".ocx", "application/octet-stream" }, - { ".oda", "application/oda" }, - { ".odc", "text/x-ms-odc" }, - { ".ods", "application/oleobject" }, - { ".oga", "audio/ogg" }, - { ".ogg", "video/ogg" }, - { ".ogv", "video/ogg" }, - { ".ogx", "application/ogg" }, - { ".one", "application/onenote" }, - { ".onea", "application/onenote" }, - { ".onetoc", "application/onenote" }, - { ".onetoc2", "application/onenote" }, - { ".onetmp", "application/onenote" }, - { ".onepkg", "application/onenote" }, - { ".osdx", "application/opensearchdescription+xml" }, - { ".otf", "font/otf" }, - { ".p10", "application/pkcs10" }, - { ".p12", "application/x-pkcs12" }, - { ".p7b", "application/x-pkcs7-certificates" }, - { ".p7c", "application/pkcs7-mime" }, - { ".p7m", "application/pkcs7-mime" }, - { ".p7r", "application/x-pkcs7-certreqresp" }, - { ".p7s", "application/pkcs7-signature" }, - { ".pbm", "image/x-portable-bitmap" }, - { ".pcx", "application/octet-stream" }, - { ".pcz", "application/octet-stream" }, - { ".pdf", "application/pdf" }, - { ".pfb", "application/octet-stream" }, - { ".pfm", "application/octet-stream" }, - { ".pfx", "application/x-pkcs12" }, - { ".pgm", "image/x-portable-graymap" }, - { ".pko", "application/vnd.ms-pki.pko" }, - { ".pma", "application/x-perfmon" }, - { ".pmc", "application/x-perfmon" }, - { ".pml", "application/x-perfmon" }, - { ".pmr", "application/x-perfmon" }, - { ".pmw", "application/x-perfmon" }, - { ".png", "image/png" }, - { ".pnm", "image/x-portable-anymap" }, - { ".pnz", "image/png" }, - { ".pot", "application/vnd.ms-powerpoint" }, - { ".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12" }, - { ".potx", "application/vnd.openxmlformats-officedocument.presentationml.template" }, - { ".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12" }, - { ".ppm", "image/x-portable-pixmap" }, - { ".pps", "application/vnd.ms-powerpoint" }, - { ".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12" }, - { ".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow" }, - { ".ppt", "application/vnd.ms-powerpoint" }, - { ".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12" }, - { ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" }, - { ".prf", "application/pics-rules" }, - { ".prm", "application/octet-stream" }, - { ".prx", "application/octet-stream" }, - { ".ps", "application/postscript" }, - { ".psd", "application/octet-stream" }, - { ".psm", "application/octet-stream" }, - { ".psp", "application/octet-stream" }, - { ".pub", "application/x-mspublisher" }, - { ".qt", "video/quicktime" }, - { ".qtl", "application/x-quicktimeplayer" }, - { ".qxd", "application/octet-stream" }, - { ".ra", "audio/x-pn-realaudio" }, - { ".ram", "audio/x-pn-realaudio" }, - { ".rar", "application/octet-stream" }, - { ".ras", "image/x-cmu-raster" }, - { ".rf", "image/vnd.rn-realflash" }, - { ".rgb", "image/x-rgb" }, - { ".rm", "application/vnd.rn-realmedia" }, - { ".rmi", "audio/mid" }, - { ".roff", "application/x-troff" }, - { ".rpm", "audio/x-pn-realaudio-plugin" }, - { ".rtf", "application/rtf" }, - { ".rtx", "text/richtext" }, - { ".scd", "application/x-msschedule" }, - { ".sct", "text/scriptlet" }, - { ".sea", "application/octet-stream" }, - { ".setpay", "application/set-payment-initiation" }, - { ".setreg", "application/set-registration-initiation" }, - { ".sgml", "text/sgml" }, - { ".sh", "application/x-sh" }, - { ".shar", "application/x-shar" }, - { ".sit", "application/x-stuffit" }, - { ".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12" }, - { ".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide" }, - { ".smd", "audio/x-smd" }, - { ".smi", "application/octet-stream" }, - { ".smx", "audio/x-smd" }, - { ".smz", "audio/x-smd" }, - { ".snd", "audio/basic" }, - { ".snp", "application/octet-stream" }, - { ".spc", "application/x-pkcs7-certificates" }, - { ".spl", "application/futuresplash" }, - { ".spx", "audio/ogg" }, - { ".src", "application/x-wais-source" }, - { ".ssm", "application/streamingmedia" }, - { ".sst", "application/vnd.ms-pki.certstore" }, - { ".stl", "application/vnd.ms-pki.stl" }, - { ".sv4cpio", "application/x-sv4cpio" }, - { ".sv4crc", "application/x-sv4crc" }, - { ".svg", "image/svg+xml" }, - { ".svgz", "image/svg+xml" }, - { ".swf", "application/x-shockwave-flash" }, - { ".t", "application/x-troff" }, - { ".tar", "application/x-tar" }, - { ".tcl", "application/x-tcl" }, - { ".tex", "application/x-tex" }, - { ".texi", "application/x-texinfo" }, - { ".texinfo", "application/x-texinfo" }, - { ".tgz", "application/x-compressed" }, - { ".thmx", "application/vnd.ms-officetheme" }, - { ".thn", "application/octet-stream" }, - { ".tif", "image/tiff" }, - { ".tiff", "image/tiff" }, - { ".toc", "application/octet-stream" }, - { ".tr", "application/x-troff" }, - { ".trm", "application/x-msterminal" }, - { ".ts", "video/vnd.dlna.mpeg-tts" }, - { ".tsv", "text/tab-separated-values" }, - { ".ttc", "application/x-font-ttf" }, - { ".ttf", "application/x-font-ttf" }, - { ".tts", "video/vnd.dlna.mpeg-tts" }, - { ".txt", "text/plain" }, - { ".u32", "application/octet-stream" }, - { ".uls", "text/iuls" }, - { ".ustar", "application/x-ustar" }, - { ".vbs", "text/vbscript" }, - { ".vcf", "text/x-vcard" }, - { ".vcs", "text/plain" }, - { ".vdx", "application/vnd.ms-visio.viewer" }, - { ".vml", "text/xml" }, - { ".vsd", "application/vnd.visio" }, - { ".vss", "application/vnd.visio" }, - { ".vst", "application/vnd.visio" }, - { ".vsto", "application/x-ms-vsto" }, - { ".vsw", "application/vnd.visio" }, - { ".vsx", "application/vnd.visio" }, - { ".vtx", "application/vnd.visio" }, - { ".wasm", "application/wasm" }, - { ".wav", "audio/wav" }, - { ".wax", "audio/x-ms-wax" }, - { ".wbmp", "image/vnd.wap.wbmp" }, - { ".wcm", "application/vnd.ms-works" }, - { ".wdb", "application/vnd.ms-works" }, - { ".webm", "video/webm" }, - { ".webmanifest", "application/manifest+json" }, // https://w3c.github.io/manifest/#media-type-registration - { ".webp", "image/webp" }, - { ".wks", "application/vnd.ms-works" }, - { ".wm", "video/x-ms-wm" }, - { ".wma", "audio/x-ms-wma" }, - { ".wmd", "application/x-ms-wmd" }, - { ".wmf", "application/x-msmetafile" }, - { ".wml", "text/vnd.wap.wml" }, - { ".wmlc", "application/vnd.wap.wmlc" }, - { ".wmls", "text/vnd.wap.wmlscript" }, - { ".wmlsc", "application/vnd.wap.wmlscriptc" }, - { ".wmp", "video/x-ms-wmp" }, - { ".wmv", "video/x-ms-wmv" }, - { ".wmx", "video/x-ms-wmx" }, - { ".wmz", "application/x-ms-wmz" }, - { ".woff", "application/font-woff" }, // https://www.w3.org/TR/WOFF/#appendix-b - { ".woff2", "font/woff2" }, // https://www.w3.org/TR/WOFF2/#IMT - { ".wps", "application/vnd.ms-works" }, - { ".wri", "application/x-mswrite" }, - { ".wrl", "x-world/x-vrml" }, - { ".wrz", "x-world/x-vrml" }, - { ".wsdl", "text/xml" }, - { ".wtv", "video/x-ms-wtv" }, - { ".wvx", "video/x-ms-wvx" }, - { ".x", "application/directx" }, - { ".xaf", "x-world/x-vrml" }, - { ".xaml", "application/xaml+xml" }, - { ".xap", "application/x-silverlight-app" }, - { ".xbap", "application/x-ms-xbap" }, - { ".xbm", "image/x-xbitmap" }, - { ".xdr", "text/plain" }, - { ".xht", "application/xhtml+xml" }, - { ".xhtml", "application/xhtml+xml" }, - { ".xla", "application/vnd.ms-excel" }, - { ".xlam", "application/vnd.ms-excel.addin.macroEnabled.12" }, - { ".xlc", "application/vnd.ms-excel" }, - { ".xlm", "application/vnd.ms-excel" }, - { ".xls", "application/vnd.ms-excel" }, - { ".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12" }, - { ".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12" }, - { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }, - { ".xlt", "application/vnd.ms-excel" }, - { ".xltm", "application/vnd.ms-excel.template.macroEnabled.12" }, - { ".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template" }, - { ".xlw", "application/vnd.ms-excel" }, - { ".xml", "text/xml" }, - { ".xof", "x-world/x-vrml" }, - { ".xpm", "image/x-xpixmap" }, - { ".xps", "application/vnd.ms-xpsdocument" }, - { ".xsd", "text/xml" }, - { ".xsf", "text/xml" }, - { ".xsl", "text/xml" }, - { ".xslt", "text/xml" }, - { ".xsn", "application/octet-stream" }, - { ".xtp", "application/octet-stream" }, - { ".xwd", "image/x-xwindowdump" }, - { ".z", "application/x-compress" }, - { ".zip", "application/x-zip-compressed" }, - }) - { - } - #endregion - - /// - /// Creates a lookup engine using the provided mapping. - /// It is recommended that the IDictionary instance use StringComparer.OrdinalIgnoreCase. - /// - /// - public FileExtensionContentTypeProvider(IDictionary mapping) - { - if (mapping == null) - { - throw new ArgumentNullException(nameof(mapping)); - } - Mappings = mapping; - } - - /// - /// The cross reference table of file extensions and content-types. - /// - public IDictionary Mappings { get; private set; } - - /// - /// Given a file path, determine the MIME type - /// - /// A file path - /// The resulting MIME type - /// True if MIME type could be determined - public bool TryGetContentType(string subpath, [MaybeNullWhen(false)] out string contentType) - { - var extension = GetExtension(subpath); - if (extension == null) - { - contentType = null; - return false; - } - return Mappings.TryGetValue(extension, out contentType); - } - - private static string GetExtension(string path) - { - // Don't use Path.GetExtension as that may throw an exception if there are - // invalid characters in the path. Invalid characters should be handled - // by the FileProviders - - if (string.IsNullOrWhiteSpace(path)) - { - return null; - } - - int index = path.LastIndexOf('.'); - if (index < 0) - { - return null; - } - - return path.Substring(index); - } - } - } - -#if !MAUI_INCLUDE - internal partial class StaticContentProvider - { - private readonly IFileProvider _fileProvider; - private readonly Uri _appBaseUri; - private readonly string _hostPageRelativePath; - private static readonly ManifestEmbeddedFileProvider _manifestProvider = - new ManifestEmbeddedFileProvider(typeof(WebViewManager).Assembly); - - public static StaticContentProvider ResolveFromStaticWebAssetsManifest(IFileProvider fileProvider, Uri uri, string hostPageRelativePath) - { - var assembly = Assembly.GetEntryAssembly(); - if (string.IsNullOrEmpty(assembly?.Location)) - { - return null; - } - - var name = Path.GetFileNameWithoutExtension(assembly.Location); - var manifestPath = Path.Combine(Path.GetDirectoryName(assembly.Location)!, $"{name}.staticwebassets.runtime.json"); - if (!File.Exists(manifestPath)) - { - return null; - } - - using var stream = File.OpenRead(Path.Combine(Path.GetDirectoryName(assembly.Location)!, $"{name}.staticwebassets.runtime.json")); - var manifest = ManifestStaticWebAssetFileProvider.StaticWebAssetManifest.Parse(stream); - var provider = new ManifestStaticWebAssetFileProvider(manifest, (p) => new PhysicalFileProvider(p)); - return new StaticContentProvider(provider, uri, hostPageRelativePath); - } - - public StaticContentProvider(IFileProvider fileProvider, Uri appBaseUri, string hostPageRelativePath) - { - _fileProvider = fileProvider ?? throw new ArgumentNullException(nameof(fileProvider)); - _appBaseUri = appBaseUri ?? throw new ArgumentNullException(nameof(appBaseUri)); - _hostPageRelativePath = hostPageRelativePath ?? throw new ArgumentNullException(nameof(hostPageRelativePath)); - } - - public bool TryGetResponseContent(string requestUri, bool allowFallbackOnHostPage, out int statusCode, out string statusMessage, out Stream content, out IDictionary headers) - { - var fileUri = new Uri(requestUri); - if (_appBaseUri.IsBaseOf(fileUri)) - { - var relativePath = _appBaseUri.MakeRelativeUri(fileUri).ToString(); - - // Content in the file provider takes first priority - // Next we may fall back on supplying the host page to support deep linking - // If there's no match, fall back on serving embedded framework content - string contentType; - var found = TryGetFromFileProvider(relativePath, out content, out contentType) - || (allowFallbackOnHostPage && TryGetFromFileProvider(_hostPageRelativePath, out content, out contentType)) - || TryGetFrameworkFile(relativePath, out content, out contentType); - - if (found) - { - statusCode = 200; - statusMessage = "OK"; - headers = GetResponseHeaders(contentType); - } - else - { - content = new MemoryStream(Encoding.UTF8.GetBytes($"There is no content at {relativePath}")); - statusCode = 404; - statusMessage = "Not found"; - headers = GetResponseHeaders("text/plain"); - } - - // Always respond to requests within the base URI, even if there's no matching file - return true; - } - else - { - // URL isn't within application base path, so let the network handle it - statusCode = default; - statusMessage = default; - headers = default; - content = default; - return false; - } - } - - private bool TryGetFromFileProvider(string relativePath, out Stream content, out string contentType) - { - if (!string.IsNullOrEmpty(relativePath)) - { - var fileInfo = _fileProvider.GetFileInfo(relativePath); - if (fileInfo.Exists) - { - content = fileInfo.CreateReadStream(); - contentType = GetResponseContentTypeOrDefault(fileInfo.Name); - return true; - } - } - - content = default; - contentType = default; - return false; - } - - private static bool TryGetFrameworkFile(string relativePath, out Stream content, out string contentType) - { - // We're not trying to simulate everything a real webserver does. We don't need to - // support querystring parameters, for example. It's enough to require an exact match. - var file = _manifestProvider.GetFileInfo(relativePath); - if (file.Exists) - { - content = file.CreateReadStream(); - contentType = GetResponseContentTypeOrDefault(relativePath); - return true; - } - - content = default; - contentType = default; - return false; - } - } -#endif -} diff --git a/src/BlazorWebView/src/WebView2/WebView2WebViewManager.cs b/src/BlazorWebView/src/WebView2/WebView2WebViewManager.cs index 198ec4bd2da1..131285c0292f 100644 --- a/src/BlazorWebView/src/WebView2/WebView2WebViewManager.cs +++ b/src/BlazorWebView/src/WebView2/WebView2WebViewManager.cs @@ -7,8 +7,6 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Web; -using Microsoft.AspNetCore.Components.WebView.WebView2.Internal; -using Microsoft.AspNetCore.Components.WebView.WebView2.Tmp; using Microsoft.Extensions.FileProviders; namespace Microsoft.AspNetCore.Components.WebView.WebView2 @@ -25,7 +23,6 @@ public class WebView2WebViewManager : WebViewManager private const string AppOrigin = "https://0.0.0.0/"; private readonly IWebView2Wrapper _webview; - private readonly StaticContentProvider _staticContentProvider = null; private readonly Task _webviewReadyTask; /// @@ -40,7 +37,6 @@ public WebView2WebViewManager(IWebView2Wrapper webview, IServiceProvider service : base(services, dispatcher, new Uri(AppOrigin), fileProvider, jsComponents, hostPageRelativePath) { _webview = webview ?? throw new ArgumentNullException(nameof(webview)); - _staticContentProvider = StaticContentProvider.ResolveFromStaticWebAssetsManifest(fileProvider, new Uri(AppOrigin), hostPageRelativePath); // Unfortunately the CoreWebView2 can only be instantiated asynchronously. // We want the external API to behave as if initalization is synchronous, @@ -78,7 +74,7 @@ private async Task InitializeWebView2() eventArgs.ResourceContext == CoreWebView2WebResourceContextWrapper.Document || eventArgs.ResourceContext == CoreWebView2WebResourceContextWrapper.Other; // e.g., dev tools requesting page source - if (TryGetResponseContentNewManifest(eventArgs.Request.Uri, allowFallbackOnHostPage, out var statusCode, out var statusMessage, out var content, out var headers)) + if (TryGetResponseContent(eventArgs.Request.Uri, allowFallbackOnHostPage, out var statusCode, out var statusMessage, out var content, out var headers)) { var headerString = GetHeaderString(headers); eventArgs.SetResponse(content, statusCode, statusMessage, headerString); @@ -136,8 +132,5 @@ private void ApplyDefaultWebViewSettings() } }); } - - private bool TryGetResponseContentNewManifest(string uri, bool allowFallbackOnHostPage, out int statusCode, out string statusMessage, out Stream content, out IDictionary headers) => - _staticContentProvider.TryGetResponseContent(uri, allowFallbackOnHostPage, out statusCode, out statusMessage, out content, out headers); } } From c0400b2d728086b8e9b2dd17ef058c42af90d51a Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Fri, 20 Aug 2021 03:50:34 -0700 Subject: [PATCH 2/6] Add targets for winforms and wpf packages --- .../src/WindowsForms/BlazorWebView.cs | 4 ++-- ...Core.Components.WebView.WindowsForms.csproj | 4 ++++ ...tCore.Components.WebView.WindowsForms.props | 2 ++ ...ore.Components.WebView.WindowsForms.targets | 18 ++++++++++++++++++ ...ft.AspNetCore.Components.WebView.Wpf.csproj | 6 +++++- ...oft.AspNetCore.Components.WebView.Wpf.props | 2 ++ ...t.AspNetCore.Components.WebView.Wpf.targets | 18 ++++++++++++++++++ 7 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 src/BlazorWebView/src/WindowsForms/build/Microsoft.AspNetCore.Components.WebView.WindowsForms.props create mode 100644 src/BlazorWebView/src/WindowsForms/build/Microsoft.AspNetCore.Components.WebView.WindowsForms.targets create mode 100644 src/BlazorWebView/src/Wpf/build/Microsoft.AspNetCore.Components.WebView.Wpf.props create mode 100644 src/BlazorWebView/src/Wpf/build/Microsoft.AspNetCore.Components.WebView.Wpf.targets diff --git a/src/BlazorWebView/src/WindowsForms/BlazorWebView.cs b/src/BlazorWebView/src/WindowsForms/BlazorWebView.cs index 3e91c66577a7..da76815d6825 100644 --- a/src/BlazorWebView/src/WindowsForms/BlazorWebView.cs +++ b/src/BlazorWebView/src/WindowsForms/BlazorWebView.cs @@ -135,8 +135,8 @@ private void StartWebViewCoreIfPossible() return; } - // We assume the host page is always in the root of the content directory, because it's - // unclear there's any other use case. We can add more options later if so. + // We assume the host page is always in the root of the content directory, because it's + // unclear there's any other use case. We can add more options later if so. var contentRootDir = Path.GetDirectoryName(Path.GetFullPath(HostPage)); var hostPageRelativePath = Path.GetRelativePath(contentRootDir, HostPage); var fileProvider = new PhysicalFileProvider(contentRootDir); diff --git a/src/BlazorWebView/src/WindowsForms/Microsoft.AspNetCore.Components.WebView.WindowsForms.csproj b/src/BlazorWebView/src/WindowsForms/Microsoft.AspNetCore.Components.WebView.WindowsForms.csproj index 1262024b956f..2fbe68fe92f9 100644 --- a/src/BlazorWebView/src/WindowsForms/Microsoft.AspNetCore.Components.WebView.WindowsForms.csproj +++ b/src/BlazorWebView/src/WindowsForms/Microsoft.AspNetCore.Components.WebView.WindowsForms.csproj @@ -20,4 +20,8 @@ + + + + diff --git a/src/BlazorWebView/src/WindowsForms/build/Microsoft.AspNetCore.Components.WebView.WindowsForms.props b/src/BlazorWebView/src/WindowsForms/build/Microsoft.AspNetCore.Components.WebView.WindowsForms.props new file mode 100644 index 000000000000..7bc91e44385a --- /dev/null +++ b/src/BlazorWebView/src/WindowsForms/build/Microsoft.AspNetCore.Components.WebView.WindowsForms.props @@ -0,0 +1,2 @@ + + diff --git a/src/BlazorWebView/src/WindowsForms/build/Microsoft.AspNetCore.Components.WebView.WindowsForms.targets b/src/BlazorWebView/src/WindowsForms/build/Microsoft.AspNetCore.Components.WebView.WindowsForms.targets new file mode 100644 index 000000000000..e7b723e4b2de --- /dev/null +++ b/src/BlazorWebView/src/WindowsForms/build/Microsoft.AspNetCore.Components.WebView.WindowsForms.targets @@ -0,0 +1,18 @@ + + + + / + Root + $(CoreCompileDependsOn);StaticWebAssetsPrepareForRun + + + + + + + + + + + + \ No newline at end of file diff --git a/src/BlazorWebView/src/Wpf/Microsoft.AspNetCore.Components.WebView.Wpf.csproj b/src/BlazorWebView/src/Wpf/Microsoft.AspNetCore.Components.WebView.Wpf.csproj index 8a84bb321d41..1078253942aa 100644 --- a/src/BlazorWebView/src/Wpf/Microsoft.AspNetCore.Components.WebView.Wpf.csproj +++ b/src/BlazorWebView/src/Wpf/Microsoft.AspNetCore.Components.WebView.Wpf.csproj @@ -1,4 +1,4 @@ - +