Merge branch 'tweakUIText_DriveDetectionWarning' of https://github.com/alekhyareddy28/PowerToys into tweakUIText_DriveDetectionWarning

This commit is contained in:
Alekhya Reddy 2020-07-22 09:55:27 -07:00
commit b193905d00
30 changed files with 1615 additions and 692 deletions

3
.gitignore vendored
View file

@ -340,4 +340,5 @@ src/common/Telemetry/*.etl
# Don't ignore MergeModules
!**/MergeModules/Release/
!**/MergeModules/Debug/
!**/MergeModules/Debug/
/src/modules/previewpane/SvgThumbnailProvider/$(SolutionDir)$(Platform)/$(Configuration)/modules/FileExplorerPreview/SvgThumbnailProvider.xml

File diff suppressed because it is too large Load diff

View file

@ -428,6 +428,23 @@
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="RuntimeVersion" Value="v4.0.30319" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="CodeBase" Value="file:///[FileExplorerPreviewInstallFolder]SvgPreviewHandler.dll" />
</RegistryKey>
<!-- Registry Key for Class Registration of Svg Thumbnail Provider -->
<RegistryKey Root="HKCR" Key="CLSID\{36B27788-A8BB-4698-A756-DF9F11F64F84}">
<RegistryValue Type="string" Value="SvgThumbnailProvider.SvgThumbnailProvider" />
<RegistryValue Type="string" Name="DisplayName" Value="Svg Thumbnail Provider" />
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
<RegistryValue Type="string" Key="Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value=""/>
<RegistryValue Type="string" Key="InprocServer32" Value="mscoree.dll" />
<RegistryValue Type="string" Key="InprocServer32" Name="Assembly" Value="SvgThumbnailProvider, Version=$(var.Version).0, Culture=neutral" />
<RegistryValue Type="string" Key="InprocServer32" Name="Class" Value="SvgThumbnailProvider.SvgThumbnailProvider" />
<RegistryValue Type="string" Key="InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" />
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Both" />
<RegistryValue Type="string" Key="InprocServer32" Name="CodeBase" Value="file:///[FileExplorerPreviewInstallFolder]SvgThumbnailProvider.dll" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Assembly" Value="SvgThumbnailProvider, Version=$(var.Version).0, Culture=neutral" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="Class" Value="SvgThumbnailProvider.SvgThumbnailProvider" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="RuntimeVersion" Value="v4.0.30319" />
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="CodeBase" Value="file:///[FileExplorerPreviewInstallFolder]SvgThumbnailProvider.dll" />
</RegistryKey>
<!-- Registry Key for Class Registration of Markdown Preview Handler -->
<RegistryKey Root="HKCR" Key="CLSID\{45769bcc-e8fd-42d0-947e-02beef77a1f5}">
<RegistryValue Type="string" Value="MarkdownPreviewHandler.MarkdownPreviewHandler" />
@ -461,6 +478,10 @@
<RegistryKey Root="HKCR" Key=".svg\shellex">
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{ddee2b8a-6807-48a6-bb20-2338174ff779}" />
</RegistryKey>
<!-- Add file type association for Svg Thumbnail Provider -->
<RegistryKey Root="HKCR" Key=".svg\shellex">
<RegistryValue Type="string" Key="{E357FCCD-A995-4576-B01F-234630154E96}" Value="{36B27788-A8BB-4698-A756-DF9F11F64F84}" />
</RegistryKey>
<!-- Add file type association for Markdown Preview Handler -->
<RegistryKey Root="HKCR" Key=".md\shellex">
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" />
@ -469,6 +490,10 @@
<RegistryKey Root="HKLM" Key="Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION">
<RegistryValue Type="integer" Name="prevhost.exe" Value="11000" />
</RegistryKey>
<!-- Update Key to use IE11 for dllhost.exe -->
<RegistryKey Root="HKLM" Key="Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION">
<RegistryValue Type="integer" Name="dllhost.exe" Value="11000" />
</RegistryKey>
</Component>
</DirectoryRef>
@ -611,6 +636,8 @@
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\Telemetry.dll" />
<!-- File to include dll for Svg Preview Handler -->
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\SvgPreviewHandler.dll" />
<!-- File to include dll for Svg Preview Handler -->
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\SvgThumbnailProvider.dll" />
<!-- Files to include dll's for Markdown Preview Handler and it's dependencies -->
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\MarkdownPreviewHandler.dll" />
<File Source="$(var.BinX64Dir)modules\FileExplorerPreview\Markdig.Signed.dll" />

View file

@ -12,36 +12,53 @@ namespace Microsoft.PowerToys.Settings.UI.Lib
{
public class PowerPreviewProperties
{
private bool enableSvg = true;
private bool enableSvgPreview = true;
[JsonPropertyName("svg-previewer-toggle-setting")]
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool EnableSvg
public bool EnableSvgPreview
{
get => this.enableSvg;
get => this.enableSvgPreview;
set
{
if (value != this.enableSvg)
if (value != this.enableSvgPreview)
{
LogTelemetryEvent(value);
this.enableSvg = value;
this.enableSvgPreview = value;
}
}
}
private bool enableSvgThumbnail = true;
[JsonPropertyName("svg-thumbnail-toggle-setting")]
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool EnableSvgThumbnail
{
get => this.enableSvgThumbnail;
set
{
if (value != this.enableSvgThumbnail)
{
LogTelemetryEvent(value);
this.enableSvgThumbnail = value;
}
}
}
private bool enableMd = true;
private bool enableMdPreview = true;
[JsonPropertyName("md-previewer-toggle-setting")]
[JsonConverter(typeof(BoolPropertyJsonConverter))]
public bool EnableMd
public bool EnableMdPreview
{
get => this.enableMd;
get => this.enableMdPreview;
set
{
if (value != this.enableMd)
if (value != this.enableMdPreview)
{
LogTelemetryEvent(value);
this.enableMd = value;
this.enableMdPreview = value;
}
}
}

View file

@ -60,9 +60,9 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Toolkit.UI.XamlHost" Version="6.1.0" />
<PackageReference Include="Microsoft.Toolkit.Wpf.UI.Controls" Version="6.1.0" />
<PackageReference Include="Microsoft.Toolkit.Wpf.UI.XamlHost" Version="6.1.0" />
<PackageReference Include="Microsoft.Toolkit.UI.XamlHost" Version="6.1.1" />
<PackageReference Include="Microsoft.Toolkit.Wpf.UI.Controls" Version="6.1.1" />
<PackageReference Include="Microsoft.Toolkit.Wpf.UI.XamlHost" Version="6.1.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View file

@ -184,10 +184,10 @@
<Version>6.1.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.Toolkit.Win32.UI.XamlApplication">
<Version>6.1.0</Version>
<Version>6.1.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.UI.Xaml">
<Version>2.5.0-prerelease.200609001</Version>
<Version>2.5.0-prerelease.200708003</Version>
</PackageReference>
<PackageReference Include="Microsoft.Xaml.Behaviors.Uwp.Managed">
<Version>2.0.1</Version>

View file

@ -422,6 +422,9 @@
</data>
<data name="FileExplorerPreview_ToggleSwitch_Preview_SVG.Header" xml:space="preserve">
<value>Enable SVG (.svg) preview</value>
</data>
<data name="FileExplorerPreview_ToggleSwitch_SVG_Thumbnail.Header" xml:space="preserve">
<value>Enable SVG (.svg) thumbnails</value>
</data>
<data name="FileExplorerPreview_Description.Text" xml:space="preserve">
<value>These settings allow you to manage your Windows File Explorer custom preview handlers.</value>

View file

@ -27,12 +27,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
SettingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName);
}
this._svgRenderIsEnabled = Settings.properties.EnableSvg;
this._mdRenderIsEnabled = Settings.properties.EnableMd;
this._svgRenderIsEnabled = Settings.properties.EnableSvgPreview;
this._svgThumbnailIsEnabled = Settings.properties.EnableSvgThumbnail;
this._mdRenderIsEnabled = Settings.properties.EnableMdPreview;
}
private bool _svgRenderIsEnabled = false;
private bool _mdRenderIsEnabled = false;
private bool _svgThumbnailIsEnabled = false;
public bool SVGRenderIsEnabled
{
@ -46,7 +48,25 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
if (value != _svgRenderIsEnabled)
{
_svgRenderIsEnabled = value;
Settings.properties.EnableSvg = value;
Settings.properties.EnableSvgPreview = value;
RaisePropertyChanged();
}
}
}
public bool SVGThumbnailIsEnabled
{
get
{
return _svgThumbnailIsEnabled;
}
set
{
if (value != _svgThumbnailIsEnabled)
{
_svgThumbnailIsEnabled = value;
Settings.properties.EnableSvgThumbnail = value;
RaisePropertyChanged();
}
}
@ -64,7 +84,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
if (value != _mdRenderIsEnabled)
{
_mdRenderIsEnabled = value;
Settings.properties.EnableMd = value;
Settings.properties.EnableMdPreview = value;
RaisePropertyChanged();
}
}

View file

@ -50,6 +50,10 @@
Margin="{StaticResource MediumTopMargin}"
IsOn="{Binding Mode=TwoWay, Path=SVGRenderIsEnabled}" />
<ToggleSwitch x:Uid="FileExplorerPreview_ToggleSwitch_SVG_Thumbnail"
Margin="{StaticResource SmallTopMargin}"
IsOn="{Binding Mode=TwoWay, Path=SVGThumbnailIsEnabled}" />
<ToggleSwitch x:Uid="FileExplorerPreview_ToggleSwitch_Preview_MD"
Margin="{StaticResource SmallTopMargin}"
IsOn="{Binding Mode=TwoWay, Path=MDRenderIsEnabled}" />

View file

@ -59,13 +59,30 @@ namespace ViewModelTests
ShellPage.DefaultSndMSGCallback = msg =>
{
SndModuleSettings<SndPowerPreviewSettings> snd = JsonSerializer.Deserialize<SndModuleSettings<SndPowerPreviewSettings>>(msg);
Assert.IsTrue(snd.powertoys.FileExplorerPreviewSettings.properties.EnableSvg);
Assert.IsTrue(snd.powertoys.FileExplorerPreviewSettings.properties.EnableSvgPreview);
};
// act
viewModel.SVGRenderIsEnabled = true;
}
[TestMethod]
public void SVGThumbnailIsEnabled_ShouldPrevHandler_WhenSuccessful()
{
// arrange
PowerPreviewViewModel viewModel = new PowerPreviewViewModel();
// Assert
ShellPage.DefaultSndMSGCallback = msg =>
{
SndModuleSettings<SndPowerPreviewSettings> snd = JsonSerializer.Deserialize<SndModuleSettings<SndPowerPreviewSettings>>(msg);
Assert.IsTrue(snd.powertoys.FileExplorerPreviewSettings.properties.EnableSvgThumbnail);
};
// act
viewModel.SVGThumbnailIsEnabled = true;
}
[TestMethod]
public void MDRenderIsEnabled_ShouldPrevHandler_WhenSuccessful()
{
@ -76,7 +93,7 @@ namespace ViewModelTests
ShellPage.DefaultSndMSGCallback = msg =>
{
SndModuleSettings<SndPowerPreviewSettings> snd = JsonSerializer.Deserialize<SndModuleSettings<SndPowerPreviewSettings>>(msg);
Assert.IsTrue(snd.powertoys.FileExplorerPreviewSettings.properties.EnableMd);
Assert.IsTrue(snd.powertoys.FileExplorerPreviewSettings.properties.EnableMdPreview);
};
// act

View file

@ -17,7 +17,6 @@ using Common.Utilities;
using Microsoft.PowerToys.Telemetry;
using PreviewHandlerCommon;
using SvgPreviewHandler.Telemetry.Events;
using SvgPreviewHandler.Utilities;
namespace SvgPreviewHandler
{

View file

@ -112,7 +112,6 @@
<Compile Include="Telemetry\Events\SvgFileHandlerLoaded.cs" />
<Compile Include="Telemetry\Events\SvgFilePreviewed.cs" />
<Compile Include="Telemetry\Events\SvgFilePreviewError.cs" />
<Compile Include="Utilities\SvgPreviewHandlerHelper.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\Telemetry.csproj">

View file

@ -0,0 +1,21 @@
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
"documentationRules": {
"companyName": "Microsoft Corporation",
"copyrightText": "Copyright (c) {companyName}\r\nThe {companyName} licenses this file to you under the MIT license.\r\nSee the LICENSE file in the project root for more information.",
"xmlHeader": false,
"headerDecoration": "",
"fileNamingConvention": "metadata",
"documentInterfaces": false,
"documentExposedElements": false,
"documentInternalElements": false
},
"layoutRules": {
"newlineAtEndOfFile": "require"
},
"orderingRules": {
"usingDirectivesPlacement": "outsideNamespace"
}
}
}

View file

@ -0,0 +1,244 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace SvgThumbnailProvider
{
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;
using Common;
using Common.ComInterlop;
using Common.Utilities;
using Microsoft.PowerToys.Telemetry;
using PreviewHandlerCommon;
/// <summary>
/// SVG Thumbnail Provider.
/// </summary>
[Guid("36B27788-A8BB-4698-A756-DF9F11F64F84")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
public class SvgThumbnailProvider : IInitializeWithStream, IThumbnailProvider
{
/// <summary>
/// Gets the stream object to access file.
/// </summary>
public IStream Stream { get; private set; }
/// <summary>
/// The maxium dimension (width or height) thumbnail we will generate.
/// </summary>
private const uint MaxThumbnailSize = 10000;
/// <summary>
/// Captures an image representation of browser contents.
/// </summary>
/// <param name="browser">The WebBrowser instance rendring the SVG.</param>
/// <param name="rectangle">The client rectangle to capture from.</param>
/// <param name="backgroundColor">The default background color to apply.</param>
/// <returns>A Bitmap representing the browser contents.</returns>
public static Bitmap GetBrowserContentImage(WebBrowser browser, Rectangle rectangle, Color backgroundColor)
{
Bitmap image = new Bitmap(rectangle.Width, rectangle.Height);
using (Graphics graphics = Graphics.FromImage(image))
{
IntPtr deviceContextHandle = IntPtr.Zero;
RECT rect = new RECT
{
Left = rectangle.Left,
Top = rectangle.Top,
Right = rectangle.Right,
Bottom = rectangle.Bottom,
};
graphics.Clear(backgroundColor);
try
{
deviceContextHandle = graphics.GetHdc();
IViewObject viewObject = browser.ActiveXInstance as IViewObject;
viewObject.Draw(1, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, deviceContextHandle, ref rect, IntPtr.Zero, IntPtr.Zero, 0);
}
finally
{
if (deviceContextHandle != IntPtr.Zero)
{
graphics.ReleaseHdc(deviceContextHandle);
}
}
}
return image;
}
/// <summary>
/// Wrap the SVG markup in HTML with a meta tag to ensure the
/// WebBrowser control is in Edge mode to enable SVG rendering.
/// We also set the padding and margin for the body to zero as
/// there is a default margin of 8.
/// </summary>
/// <param name="content">The content to render.</param>
/// <param name="cx">The maximum thumbnail size, in pixels.</param>
/// <returns>A thumbnail of the rendered content.</returns>
public static Bitmap GetThumbnail(string content, uint cx)
{
if (cx > MaxThumbnailSize)
{
return null;
}
Bitmap thumbnail = null;
// Wrap the SVG content in HTML in IE Edge mode so we can ensure
// we render properly.
string wrappedContent = WrapSVGInHTML(content);
WebBrowserExt browser = new WebBrowserExt();
browser.Dock = DockStyle.Fill;
browser.IsWebBrowserContextMenuEnabled = false;
browser.ScriptErrorsSuppressed = true;
browser.ScrollBarsEnabled = false;
browser.AllowNavigation = false;
browser.Width = (int)cx;
browser.Height = (int)cx;
browser.DocumentText = wrappedContent;
// Wait for the browser to render the content.
while (browser.IsBusy || browser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
// Check size of the rendered SVG.
var svg = browser.Document.GetElementsByTagName("svg").Cast<HtmlElement>().FirstOrDefault();
if (svg != null)
{
// Update the size of the browser control to fit the SVG
// in the visible viewport.
browser.Width = svg.OffsetRectangle.Width;
browser.Height = svg.OffsetRectangle.Height;
// Wait for the browser to render the content.
while (browser.IsBusy || browser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
// Capture the image of the SVG from the browser.
thumbnail = GetBrowserContentImage(browser, svg.OffsetRectangle, Color.White);
if (thumbnail.Width != cx && thumbnail.Height != cx)
{
// We are not the appropriate size for caller. Resize now while
// respecting the aspect ratio.
float scale = Math.Min((float)cx / thumbnail.Width, (float)cx / thumbnail.Height);
int scaleWidth = (int)(thumbnail.Width * scale);
int scaleHeight = (int)(thumbnail.Height * scale);
thumbnail = ResizeImage(thumbnail, scaleWidth, scaleHeight);
}
}
return thumbnail;
}
/// <summary>
/// Wrap the SVG markup in HTML with a meta tag to ensure the
/// WebBrowser control is in Edge mode to enable SVG rendering.
/// We also set the padding and margin for the body to zero as
/// there is a default margin of 8.
/// </summary>
/// <param name="svg">The original SVG markup.</param>
/// <returns>The SVG content wrapped in HTML.</returns>
public static string WrapSVGInHTML(string svg)
{
string html = @"
<head>
<meta http-equiv='X-UA-Compatible' content='IE=Edge'>
</head>
<html>
<body style='padding:0px;margin:0px;' scroll='no'>
{0}
</body>
</html>";
return string.Format(html, svg);
}
/// <summary>
/// Resize the image with high quality to the specified width and height.
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
public static Bitmap ResizeImage(Image image, int width, int height)
{
if (width <= 0 || height <= 0 ||
width > MaxThumbnailSize || height > MaxThumbnailSize)
{
return null;
}
Bitmap destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.Clear(Color.White);
graphics.DrawImage(image, 0, 0, width, height);
}
return destImage;
}
/// <inheritdoc/>
public void Initialize(IStream pstream, uint grfMode)
{
// Ignore the grfMode always use read mode to access the file.
this.Stream = pstream;
}
/// <inheritdoc/>
public void GetThumbnail(uint cx, out IntPtr phbmp, out WTS_ALPHATYPE pdwAlpha)
{
phbmp = IntPtr.Zero;
pdwAlpha = WTS_ALPHATYPE.WTSAT_UNKNOWN;
if (cx == 0 || cx > MaxThumbnailSize)
{
return;
}
string svgData = null;
using (var stream = new StreamWrapper(this.Stream as IStream))
{
using (var reader = new StreamReader(stream))
{
svgData = reader.ReadToEnd();
}
}
if (svgData != null)
{
Bitmap thumbnail = GetThumbnail(svgData, cx);
if (thumbnail != null && thumbnail.Size.Width > 0 && thumbnail.Size.Height > 0)
{
phbmp = thumbnail.GetHbitmap();
pdwAlpha = WTS_ALPHATYPE.WTSAT_RGB;
}
}
}
}
}

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8FFE09DA-FA4F-4EE1-B3A2-AD5497FBD1AD}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>SVGThumbnailProvider</RootNamespace>
<AssemblyName>SVGThumbnailProvider</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\..\x64\Debug\modules\FileExplorerPreview\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>2</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DocumentationFile>%24%28SolutionDir%29%24%28Platform%29\%24%28Configuration%29\modules\FileExplorerPreview\SvgThumbnailProvider.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\..\..\x64\Release\modules\FileExplorerPreview\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>%24%28SolutionDir%29%24%28Platform%29\%24%28Configuration%29\modules\FileExplorerPreview\SvgThumbnailProvider.xml</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\..\x64\Debug\modules\FileExplorerPreview\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\..\..\..\x64\Release\modules\FileExplorerPreview\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="SvgThumbnailProvider.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\Telemetry.csproj">
<Project>{5d00d290-4016-4cfe-9e41-1e7c724509ba}</Project>
<Name>Telemetry</Name>
</ProjectReference>
<ProjectReference Include="..\Common\PreviewHandlerCommon.csproj">
<Project>{af2349b8-e5b6-4004-9502-687c1c7730b1}</Project>
<Name>PreviewHandlerCommon</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="StyleCop.json" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -3,7 +3,7 @@
// See the LICENSE file in the project root for more information.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SvgPreviewHandler.Utilities;
using Common.Utilities;
using System.Text;
namespace UnitTests_SvgPreviewHandler

View file

@ -0,0 +1,178 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using Moq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Common.ComInterlop;
using SvgThumbnailProvider;
namespace SvgThumbnailProviderUnitTests
{
[TestClass]
public class SvgThumbnailProviderTests
{
[TestMethod]
public void LoadSimpleSVG_ShouldReturnNonNullBitmap()
{
var svgBuilder = new StringBuilder();
svgBuilder.AppendLine("<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">");
svgBuilder.AppendLine("\t<circle cx=\"50\" cy=\"50\" r=\"50\">");
svgBuilder.AppendLine("\t</circle>");
svgBuilder.AppendLine("</svg>");
Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
Assert.IsTrue(thumbnail != null);
}
[TestMethod]
public void CheckBlockedElements_ShouldReturnNullBitmap_IfBlockedElementsIsPresentInNestedLevel()
{
var svgBuilder = new StringBuilder();
svgBuilder.AppendLine("<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">");
svgBuilder.AppendLine("\t<circle cx=\"50\" cy=\"50\" r=\"50\">");
svgBuilder.AppendLine("\t\t<script>alert(\"valid-message\")</script>");
svgBuilder.AppendLine("\t</circle>");
svgBuilder.AppendLine("</svg>");
Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
Assert.IsTrue(thumbnail == null);
}
[TestMethod]
public void CheckNoSvg_ShouldReturnNullBitmap()
{
var svgBuilder = new StringBuilder();
svgBuilder.AppendLine("<p>foo</p>");
Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
Assert.IsTrue(thumbnail == null);
}
[TestMethod]
public void CheckNoSvgEmptyString_ShouldReturnNullBitmap()
{
Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail("", 256);
Assert.IsTrue(thumbnail == null);
}
[TestMethod]
public void CheckNoSvgNullString_ShouldReturnNullBitmap()
{
Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(null, 256);
Assert.IsTrue(thumbnail == null);
}
[TestMethod]
public void CheckZeroSizedThumbnail_ShouldReturnNullBitmap()
{
string content = "<svg></svg>";
Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(content, 0);
Assert.IsTrue(thumbnail == null);
}
[TestMethod]
public void CheckBlockedElements_ShouldReturnBitmap_HTMLWrapped()
{
var svgBuilder = new StringBuilder();
svgBuilder.AppendLine("<html>");
svgBuilder.AppendLine("<head>");
svgBuilder.AppendLine("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge\">");
svgBuilder.AppendLine("<meta http-equiv=\"Content-Type\" content=\"text/html\" charset=\"utf-8\">");
svgBuilder.AppendLine("</head>");
svgBuilder.AppendLine("<body>");
svgBuilder.AppendLine("<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">");
svgBuilder.AppendLine("<circle cx=\"50\" cy=\"50\" r=\"50\">");
svgBuilder.AppendLine("</circle>");
svgBuilder.AppendLine("</svg>");
svgBuilder.AppendLine("</body>");
svgBuilder.AppendLine("</html>");
Bitmap thumbnail = SvgThumbnailProvider.SvgThumbnailProvider.GetThumbnail(svgBuilder.ToString(), 256);
Assert.IsTrue(thumbnail != null);
}
[TestMethod]
public void GetThumbnail_ValidStreamSVG()
{
var svgBuilder = new StringBuilder();
svgBuilder.AppendLine("<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">");
svgBuilder.AppendLine("<circle cx=\"50\" cy=\"50\" r=\"50\">");
svgBuilder.AppendLine("</circle>");
svgBuilder.AppendLine("</svg>");
SvgThumbnailProvider.SvgThumbnailProvider provider = new SvgThumbnailProvider.SvgThumbnailProvider();
provider.Initialize(GetMockStream(svgBuilder.ToString()), 0);
IntPtr bitmap;
WTS_ALPHATYPE alphaType;
provider.GetThumbnail(256, out bitmap, out alphaType);
Assert.IsTrue(bitmap != IntPtr.Zero);
Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_RGB);
}
[TestMethod]
public void GetThumbnail_ValidStreamHTML()
{
var svgBuilder = new StringBuilder();
svgBuilder.AppendLine("<html>");
svgBuilder.AppendLine("<head>");
svgBuilder.AppendLine("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=Edge\">");
svgBuilder.AppendLine("<meta http-equiv=\"Content-Type\" content=\"text/html\" charset=\"utf-8\">");
svgBuilder.AppendLine("</head>");
svgBuilder.AppendLine("<body>");
svgBuilder.AppendLine("<svg viewBox=\"0 0 100 100\" xmlns=\"http://www.w3.org/2000/svg\">");
svgBuilder.AppendLine("<circle cx=\"50\" cy=\"50\" r=\"50\">");
svgBuilder.AppendLine("</circle>");
svgBuilder.AppendLine("</svg>");
svgBuilder.AppendLine("</body>");
svgBuilder.AppendLine("</html>");
SvgThumbnailProvider.SvgThumbnailProvider provider = new SvgThumbnailProvider.SvgThumbnailProvider();
provider.Initialize(GetMockStream(svgBuilder.ToString()), 0);
IntPtr bitmap;
WTS_ALPHATYPE alphaType;
provider.GetThumbnail(256, out bitmap, out alphaType);
Assert.IsTrue(bitmap != IntPtr.Zero);
Assert.IsTrue(alphaType == WTS_ALPHATYPE.WTSAT_RGB);
}
private IStream GetMockStream(string streamData)
{
var mockStream = new Mock<IStream>();
var streamBytes = Encoding.UTF8.GetBytes(streamData);
var streamMock = new Mock<IStream>();
var firstCall = true;
streamMock
.Setup(x => x.Read(It.IsAny<byte[]>(), It.IsAny<int>(), It.IsAny<IntPtr>()))
.Callback<byte[], int, IntPtr>((buffer, countToRead, bytesReadPtr) =>
{
if (firstCall)
{
Array.Copy(streamBytes, 0, buffer, 0, streamBytes.Length);
Marshal.WriteInt32(bytesReadPtr, streamBytes.Length);
firstCall = false;
}
else
{
Marshal.WriteInt32(bytesReadPtr, 0);
}
});
return streamMock.Object;
}
}
}

View file

@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\..\..\..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1EF1EEF0-10F0-4F2E-8550-39B6D8044D3E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>UnitTests_SvgThumbnailProvider</RootNamespace>
<AssemblyName>UnitTests-SvgThumbnailProvider</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\MSTest.TestFramework.2.1.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="Moq, Version=4.14.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Moq.4.14.5\lib\net45\Moq.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="SvgThumbnailProviderTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\PreviewHandlerCommon.csproj">
<Project>{af2349b8-e5b6-4004-9502-687c1c7730b1}</Project>
<Name>PreviewHandlerCommon</Name>
</ProjectReference>
<ProjectReference Include="..\SvgThumbnailProvider\SvgThumbnailProvider.csproj">
<Project>{8ffe09da-fa4f-4ee1-b3a2-ad5497fbd1ad}</Project>
<Name>SvgThumbnailProvider</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\..\..\..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\..\..\..\packages\MSTest.TestAdapter.2.1.1\build\net45\MSTest.TestAdapter.targets')" />
</Project>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Castle.Core" version="4.4.0" targetFramework="net472" />
<package id="Moq" version="4.14.5" targetFramework="net472" />
<package id="MSTest.TestAdapter" version="2.1.1" targetFramework="net472" />
<package id="MSTest.TestFramework" version="2.1.1" targetFramework="net472" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net472" />
<package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net472" />
</packages>

View file

@ -103,6 +103,8 @@
<Compile Include="cominterop\IPreviewHandler.cs" />
<Compile Include="cominterop\IPreviewHandlerFrame.cs" />
<Compile Include="cominterop\IPreviewHandlerVisuals.cs" />
<Compile Include="cominterop\IThumbnailProvider.cs" />
<Compile Include="cominterop\IViewObject.cs" />
<Compile Include="cominterop\LOGFONT.cs" />
<Compile Include="cominterop\MSG.cs" />
<Compile Include="cominterop\RECT.cs" />
@ -125,6 +127,7 @@
<Compile Include="handlers\StreamBasedPreviewHandler.cs" />
<Compile Include="examplehandler\TestCustomHandler.cs" />
<Compile Include="Utilities\StreamWrapper.cs" />
<Compile Include="Utilities\SvgPreviewHandlerHelper.cs" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\..\codeAnalysis\StyleCop.json">

View file

@ -1,66 +1,66 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace SvgPreviewHandler.Utilities
{
/// <summary>
/// Helper utilities for Svg Preview Handler.
/// </summary>
public class SvgPreviewHandlerHelper
{
/// <summary>
/// Dictionary of elements in lower case that are blocked from Svg for preview pane.
/// Reference for list of Svg Elements: https://developer.mozilla.org/en-US/docs/Web/SVG/Element.
/// </summary>
private static Dictionary<string, bool> blockedElementsName = new Dictionary<string, bool>
{
{ "script", true },
{ "image", true },
{ "feimage", true },
};
/// <summary>
/// Check if any of the blocked elements present in Svg.
/// </summary>
/// <param name="svgData">Input Svg.</param>
/// <returns>Returns true in case any of the blocked element is present otherwise false.</returns>
public static bool CheckBlockedElements(string svgData)
{
bool foundBlockedElement = false;
if (string.IsNullOrWhiteSpace(svgData))
{
return foundBlockedElement;
}
// Check if any of the blocked element is present. If failed to parse or iterate over Svg return default false.
// No need to throw because all the external content and script are blocked on the Web Browser Control itself.
try
{
var doc = XDocument.Parse(svgData);
var elements = doc.Descendants().ToList();
foreach (XElement element in elements)
{
var elementName = element?.Name?.LocalName?.ToLower();
if (elementName != null && blockedElementsName.ContainsKey(elementName))
{
foundBlockedElement = true;
// No need to iterate further since we are displaying info bar with condition of atleast one occurrence of blocked element is present.
break;
}
}
}
catch (Exception)
{
}
return foundBlockedElement;
}
}
}
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Common.Utilities
{
/// <summary>
/// Helper utilities for Svg Preview Handler.
/// </summary>
public class SvgPreviewHandlerHelper
{
/// <summary>
/// Dictionary of elements in lower case that are blocked from Svg for preview pane.
/// Reference for list of Svg Elements: https://developer.mozilla.org/en-US/docs/Web/SVG/Element.
/// </summary>
private static Dictionary<string, bool> blockedElementsName = new Dictionary<string, bool>
{
{ "script", true },
{ "image", true },
{ "feimage", true },
};
/// <summary>
/// Check if any of the blocked elements present in Svg.
/// </summary>
/// <param name="svgData">Input Svg.</param>
/// <returns>Returns true in case any of the blocked element is present otherwise false.</returns>
public static bool CheckBlockedElements(string svgData)
{
bool foundBlockedElement = false;
if (string.IsNullOrWhiteSpace(svgData))
{
return foundBlockedElement;
}
// Check if any of the blocked element is present. If failed to parse or iterate over Svg return default false.
// No need to throw because all the external content and script are blocked on the Web Browser Control itself.
try
{
var doc = XDocument.Parse(svgData);
var elements = doc.Descendants().ToList();
foreach (XElement element in elements)
{
var elementName = element?.Name?.LocalName?.ToLower();
if (elementName != null && blockedElementsName.ContainsKey(elementName))
{
foundBlockedElement = true;
// No need to iterate further since we are displaying info bar with condition of atleast one occurrence of blocked element is present.
break;
}
}
}
catch (Exception)
{
}
return foundBlockedElement;
}
}
}

View file

@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// Specifies the alpha type of the image.
/// </summary>
public enum WTS_ALPHATYPE : int
{
/// <summary>
/// he bitmap is an unknown format. The Shell tries nonetheless to detect whether the image has an alpha channel.
/// </summary>
WTSAT_UNKNOWN = 0,
/// <summary>
/// The bitmap is an RGB image without alpha. The alpha channel is invalid and the Shell ignores it.
/// </summary>
WTSAT_RGB = 1,
/// <summary>
/// The bitmap is an ARGB image with a valid alpha channel.
/// </summary>
WTSAT_ARGB = 2,
}
/// <summary>
/// Exposes methods for thumbnail provider.
/// </summary>
[ComImport]
[Guid("e357fccd-a995-4576-b01f-234630154e96")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IThumbnailProvider
{
/// <summary>
/// Gets a thumbnail image and alpha type.
/// </summary>
/// <param name="cx">The maximum thumbnail size, in pixels.</param>
/// <param name="phbmp">When this method returns, contains a pointer to the thumbnail image handle.</param>
/// <param name="pdwAlpha">When this method returns, contains a pointer to one of the values from the WTS_ALPHATYPE enumeration.</param>
void GetThumbnail(uint cx, out IntPtr phbmp, out WTS_ALPHATYPE pdwAlpha);
}
}

View file

@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Common.ComInterlop
{
/// <summary>
/// Enables an object to display itself directly without passing a data object to the caller.
/// </summary>
[ComImport]
[Guid("0000010D-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IViewObject
{
/// <summary>
/// Draws a representation of an object onto the specified device context.
/// </summary>
/// <param name="dwAspect">Specifies the aspect to be drawn, that is, how the object is to be represented.</param>
/// <param name="lindex">Portion of the object that is of interest for the draw operation.</param>
/// <param name="pvAspect">Pointer to additional information in a DVASPECTINFO structure that enables drawing optimizations depending on the aspect specified.</param>
/// <param name="ptd">Pointer to the DVTARGETDEVICE structure that describes the device for which the object is to be rendered.</param>
/// <param name="hdcTargetDev">Information context for the target device indicated by the ptd parameter from which the object can extract device metrics and test the device's capabilities.</param>
/// <param name="hdcDraw">Device context on which to draw. For a windowless object, the hdcDraw parameter should be in MM_TEXT mapping mode with its logical coordinates matching the client coordinates of the containing window.</param>
/// <param name="lprcBounds">Pointer to a RECTL structure specifying the rectangle on hdcDraw and in which the object should be drawn.</param>
/// <param name="lprcWBounds">If hdcDraw is a metafile device context, pointer to a RECTL structure specifying the bounding rectangle in the underlying metafile.</param>
/// <param name="pfnContinue">Pointer to a callback function that the view object should call periodically during a lengthy drawing operation to determine whether the operation should continue or be canceled.</param>
/// <param name="dwContinue">Value to pass as a parameter to the function pointed to by the pfnContinue parameter.</param>
void Draw([MarshalAs(UnmanagedType.U4)] uint dwAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, IntPtr hdcTargetDev, IntPtr hdcDraw, [MarshalAs(UnmanagedType.Struct)] ref RECT lprcBounds, [In] IntPtr lprcWBounds, IntPtr pfnContinue, [MarshalAs(UnmanagedType.U4)] uint dwContinue);
}
}

View file

@ -16,8 +16,15 @@ const CLSID CLSID_SHIMActivateMdPreviewHandler = { 0xE0907A95, 0x6F9A, 0x4D1B, {
// 45769bcc-e8fd-42d0-947e-02beef77a1f5
const CLSID CLSID_MdPreviewHandler = { 0x45769bcc, 0xe8fd, 0x42d0, { 0x94, 0x7e, 0x02, 0xbe, 0xef, 0x77, 0xa1, 0xf5 } };
// 9C723B8C-4F5C-4147-9DE4-C2808F9AF66B
const CLSID CLSID_SHIMActivateSvgThumbnailProvider = { 0x9C723B8C, 0x4F5C, 0x4147, { 0x9D, 0xE4, 0xC2, 0x80, 0x8F, 0x9A, 0xF6, 0x6B } };
// 36B27788-A8BB-4698-A756-DF9F11F64F84
const CLSID CLSID_SvgThumbnailProvider = { 0x36B27788, 0xA8BB, 0x4698, { 0xA7, 0x56, 0xDF, 0x9F, 0x11, 0xF6, 0x4F, 0x84 } };
// Pairs of NativeClsid vs ManagedClsid used for preview handlers.
const std::vector<std::pair<CLSID, CLSID>> NativeToManagedClsid({
{ CLSID_SHIMActivateMdPreviewHandler, CLSID_MdPreviewHandler },
{ CLSID_SHIMActivateSvgPreviewHandler, CLSID_SvgPreviewHandler }
{ CLSID_SHIMActivateSvgPreviewHandler, CLSID_SvgPreviewHandler },
{ CLSID_SHIMActivateSvgThumbnailProvider, CLSID_SvgThumbnailProvider }
});

View file

@ -26,6 +26,20 @@ void PowerPreviewModule::destroy()
}
}
for (auto thumbnailProvider : this->m_thumbnailProviders)
{
if (thumbnailProvider != NULL)
{
// Disable all the active thumbnail providers.
if (this->m_enabled && thumbnailProvider->GetToggleSettingState())
{
thumbnailProvider->DisablePreview();
}
delete thumbnailProvider;
}
}
delete this;
}
@ -67,6 +81,15 @@ bool PowerPreviewModule::get_config(_Out_ wchar_t* buffer, _Out_ int* buffer_siz
previewHandler->GetToggleSettingState());
}
for (auto thumbnailProvider : this->m_thumbnailProviders)
{
settings.add_bool_toggle(
thumbnailProvider->GetToggleSettingName(),
thumbnailProvider->GetToggleSettingDescription(),
thumbnailProvider->GetToggleSettingState());
}
return settings.serialize_to_buffer(buffer, buffer_size);
}
@ -82,6 +105,11 @@ void PowerPreviewModule::set_config(const wchar_t* config)
previewHandler->UpdateState(settings, this->m_enabled);
}
for (auto thumbnailProvider : this->m_thumbnailProviders)
{
thumbnailProvider->UpdateState(settings, this->m_enabled);
}
settings.save_to_settings_file();
}
catch (std::exception const& e)
@ -106,6 +134,19 @@ void PowerPreviewModule::enable()
}
}
for (auto thumbnailProvider : this->m_thumbnailProviders)
{
if (thumbnailProvider->GetToggleSettingState())
{
// Enable all the thumbnail providers with initial state set as true.
thumbnailProvider->EnableThumbnailProvider();
}
else
{
thumbnailProvider->DisableThumbnailProvider();
}
}
if (!this->m_enabled)
{
Trace::EnabledPowerPreview(true);
@ -122,6 +163,11 @@ void PowerPreviewModule::disable()
previewHandler->DisablePreview();
}
for (auto thumbnailProvider : this->m_thumbnailProviders)
{
thumbnailProvider->DisableThumbnailProvider();
}
if (this->m_enabled)
{
Trace::EnabledPowerPreview(false);
@ -156,6 +202,12 @@ void PowerPreviewModule::init_settings()
{
previewHandler->LoadState(settings);
}
for (auto thumbnailProvider : this->m_thumbnailProviders)
{
thumbnailProvider->LoadState(settings);
}
}
catch (std::exception const& e)
{

View file

@ -17,6 +17,7 @@ private:
bool m_enabled = false;
std::wstring m_moduleName;
std::vector<FileExplorerPreviewSettings *> m_previewHandlers;
std::vector<FileExplorerPreviewSettings*> m_thumbnailProviders;
public:
PowerPreviewModule() :
@ -39,6 +40,16 @@ public:
L"{45769bcc-e8fd-42d0-947e-02beef77a1f5}",
L"Markdown Preview Handler",
new RegistryWrapper())
}),
m_thumbnailProviders(
{ // TODO: MOVE THIS SVG Thumbnail Provider settings object.
new FileExplorerPreviewSettings(
true,
L"svg-thumbnail-toggle-setting",
GET_RESOURCE_STRING(IDS_SVG_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
L"{36B27788-A8BB-4698-A756-DF9F11F64F84}",
L"SVG Thumbnail Provider",
new RegistryWrapper())
})
{
init_settings();

View file

@ -95,6 +95,7 @@ BEGIN
IDS_PREVPANE_SVG_BOOL_TOGGLE_CONTROLL L"IDS_PREVPANE_SVG_BOOL_TOGGLE_CONTROLL"
IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION L"Svg Previewer"
IDS_PREVPANE_SVG_SETTINGS_DISPLAYNAME L"SVG Previewer"
IDS_SVG_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION L"Svg Thumbnail Provider"
IDS_EXPLR_SVG_BOOL_TOGGLE_CONTROLL L"EXPLR_SVG_BOOL_TOGGLE_CONTROLL"
IDS_EXPLR_SVG_SETTINGS_DESCRIPTION L"Render SVG images"
END

View file

@ -29,6 +29,7 @@
#define IDS_EXPLR_SVG_SETTINGS_DESCRIPTION 214
#define IDS_PREVPANE_SVG_SETTINGS_DISPLAYNAME 215
#define IDS_PREVPANE_MD_SETTINGS_DISPLAYNAME 216
#define IDS_SVG_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION 217
// Next default values for new objects
//

View file

@ -14,6 +14,9 @@ namespace PowerPreviewSettings
// Relative(HKLM/HKCU) sub key path of Preview Handlers list in registry.
static LPCWSTR preview_handlers_subkey = L"Software\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers";
// Relative HKCR sub key path of SVG thumbnail provider in registry
static LPCWSTR svg_thumbnail_provider_subkey = L".svg\\shellex\\{E357FCCD-A995-4576-B01F-234630154E96}";
// Base Settings Class Implementation
FileExplorerPreviewSettings::FileExplorerPreviewSettings(bool toggleSettingEnabled, const std::wstring& toggleSettingName, const std::wstring& toggleSettingDescription, LPCWSTR clsid, const std::wstring& registryValueData, RegistryWrapperIface* registryWrapper) :
m_toggleSettingEnabled(toggleSettingEnabled),
@ -127,4 +130,16 @@ namespace PowerPreviewSettings
// Delete the registry key to disable preview.
return this->m_registryWrapper->DeleteRegistryValue(HKEY_CURRENT_USER, preview_handlers_subkey, this->GetCLSID());
}
LONG FileExplorerPreviewSettings::EnableThumbnailProvider()
{
// Add registry value to enable thumbnail provider.
return this->m_registryWrapper->SetRegistryValue(HKEY_CURRENT_USER, svg_thumbnail_provider_subkey, this->GetCLSID(), REG_SZ, (LPBYTE)this->GetRegistryValueData().c_str(), (DWORD)(this->GetRegistryValueData().length() * sizeof(wchar_t)));
}
LONG FileExplorerPreviewSettings::DisableThumbnailProvider()
{
// Delete the registry key to disable thumbnail provider.
return this->m_registryWrapper->DeleteRegistryValue(HKEY_CURRENT_USER, svg_thumbnail_provider_subkey, this->GetCLSID());
}
}

View file

@ -7,24 +7,24 @@
namespace PowerPreviewSettings
{
// PowerToy Windows Explore File Preview Settings.
class FileExplorerPreviewSettings
{
private:
bool m_toggleSettingEnabled;
// PowerToy Windows Explorer File Preview Settings.
class FileExplorerPreviewSettings
{
private:
bool m_toggleSettingEnabled;
std::wstring m_toggleSettingName;
std::wstring m_toggleSettingDescription;
std::wstring m_registryValueData;
std::wstring m_registryValueData;
RegistryWrapperIface * m_registryWrapper;
LPCWSTR m_clsid;
LPCWSTR m_clsid;
public:
public:
FileExplorerPreviewSettings(bool toggleSettingEnabled, const std::wstring& toggleSettingName, const std::wstring& toggleSettingDescription, LPCWSTR clsid, const std::wstring& registryValueData, RegistryWrapperIface* registryWrapper);
~ FileExplorerPreviewSettings();
virtual bool GetToggleSettingState() const;
virtual bool GetToggleSettingState() const;
virtual void UpdateToggleSettingState(bool state);
virtual std::wstring GetToggleSettingName() const;
virtual std::wstring GetToggleSettingName() const;
virtual std::wstring GetToggleSettingDescription() const;
virtual LPCWSTR GetCLSID() const;
virtual std::wstring GetRegistryValueData() const;
@ -32,5 +32,7 @@ namespace PowerPreviewSettings
virtual void UpdateState(PowerToysSettings::PowerToyValues& settings, bool enabled);
virtual LONG EnablePreview();
virtual LONG DisablePreview();
};
virtual LONG EnableThumbnailProvider();
virtual LONG DisableThumbnailProvider();
};
}