diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/UriHelper/ExtendedUriParserTests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/UriHelper/ExtendedUriParserTests.cs
index 8da6c03a02..8a960a5f18 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/UriHelper/ExtendedUriParserTests.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri.UnitTests/UriHelper/ExtendedUriParserTests.cs
@@ -11,62 +11,85 @@ namespace Microsoft.Plugin.Uri.UnitTests.UriHelper
public class ExtendedUriParserTests
{
[DataTestMethod]
- [DataRow("google.com", true, "https://google.com/")]
- [DataRow("http://google.com", true, "http://google.com/")]
- [DataRow("localhost", true, "https://localhost/")]
- [DataRow("http://localhost", true, "http://localhost/")]
- [DataRow("127.0.0.1", true, "https://127.0.0.1/")]
- [DataRow("http://127.0.0.1", true, "http://127.0.0.1/")]
- [DataRow("http://127.0.0.1:80", true, "http://127.0.0.1/")]
- [DataRow("127", false, null)]
- [DataRow("", false, null)]
- [DataRow("https://google.com", true, "https://google.com/")]
- [DataRow("ftps://google.com", true, "ftps://google.com/")]
- [DataRow(null, false, null)]
- [DataRow("bing.com/search?q=gmx", true, "https://bing.com/search?q=gmx")]
- [DataRow("http://bing.com/search?q=gmx", true, "http://bing.com/search?q=gmx")]
- [DataRow("h", true, "https://h/")]
- [DataRow("http://h", true, "http://h/")]
- [DataRow("ht", true, "https://ht/")]
- [DataRow("http://ht", true, "http://ht/")]
- [DataRow("htt", true, "https://htt/")]
- [DataRow("http://htt", true, "http://htt/")]
- [DataRow("http", true, "https://http/")]
- [DataRow("http://http", true, "http://http/")]
- [DataRow("http:", false, null)]
- [DataRow("http:/", false, null)]
- [DataRow("http://", false, null)]
- [DataRow("http://t", true, "http://t/")]
- [DataRow("http://te", true, "http://te/")]
- [DataRow("http://tes", true, "http://tes/")]
- [DataRow("http://test", true, "http://test/")]
- [DataRow("http://test.", false, null)]
- [DataRow("http://test.c", true, "http://test.c/")]
- [DataRow("http://test.co", true, "http://test.co/")]
- [DataRow("http://test.com", true, "http://test.com/")]
- [DataRow("http:3", true, "https://http:3/")]
- [DataRow("http://http:3", true, "http://http:3/")]
- [DataRow("[::]", true, "https://[::]/")]
- [DataRow("http://[::]", true, "http://[::]/")]
- [DataRow("[2001:0DB8::1]", true, "https://[2001:db8::1]/")]
- [DataRow("http://[2001:0DB8::1]", true, "http://[2001:db8::1]/")]
- [DataRow("[2001:0DB8::1]:80", true, "https://[2001:db8::1]/")]
- [DataRow("http://[2001:0DB8::1]:80", true, "http://[2001:db8::1]/")]
- [DataRow("mailto:example@mail.com", true, "mailto:example@mail.com")]
- [DataRow("tel:411", true, "tel:411")]
- [DataRow("ftp://example.com", true, "ftp://example.com/")]
- [DataRow("example.com:443", true, "example.com:443")]
+ [DataRow("google.com", true, "https://google.com/", true)]
+ [DataRow("http://google.com", true, "http://google.com/", true)]
+ [DataRow("localhost", true, "https://localhost/", true)]
+ [DataRow("http://localhost", true, "http://localhost/", true)]
+ [DataRow("127.0.0.1", true, "https://127.0.0.1/", true)]
+ [DataRow("http://127.0.0.1", true, "http://127.0.0.1/", true)]
+ [DataRow("http://127.0.0.1:80", true, "http://127.0.0.1/", true)]
+ [DataRow("127", false, null, false)]
+ [DataRow("", false, null, false)]
+ [DataRow("https://google.com", true, "https://google.com/", true)]
+ [DataRow("ftps://google.com", true, "ftps://google.com/", false)]
+ [DataRow(null, false, null, false)]
+ [DataRow("bing.com/search?q=gmx", true, "https://bing.com/search?q=gmx", true)]
+ [DataRow("http://bing.com/search?q=gmx", true, "http://bing.com/search?q=gmx", true)]
+ [DataRow("h", true, "https://h/", true)]
+ [DataRow("http://h", true, "http://h/", true)]
+ [DataRow("ht", true, "https://ht/", true)]
+ [DataRow("http://ht", true, "http://ht/", true)]
+ [DataRow("htt", true, "https://htt/", true)]
+ [DataRow("http://htt", true, "http://htt/", true)]
+ [DataRow("http", true, "https://http/", true)]
+ [DataRow("http://http", true, "http://http/", true)]
+ [DataRow("http:", false, null, false)]
+ [DataRow("http:/", false, null, false)]
+ [DataRow("http://", false, null, false)]
+ [DataRow("http://t", true, "http://t/", true)]
+ [DataRow("http://te", true, "http://te/", true)]
+ [DataRow("http://tes", true, "http://tes/", true)]
+ [DataRow("http://test", true, "http://test/", true)]
+ [DataRow("http://test.", false, null, false)]
+ [DataRow("http://test.c", true, "http://test.c/", true)]
+ [DataRow("http://test.co", true, "http://test.co/", true)]
+ [DataRow("http://test.com", true, "http://test.com/", true)]
+ [DataRow("http:3", true, "https://http:3/", true)]
+ [DataRow("http://http:3", true, "http://http:3/", true)]
+ [DataRow("[::]", true, "https://[::]/", true)]
+ [DataRow("http://[::]", true, "http://[::]/", true)]
+ [DataRow("[2001:0DB8::1]", true, "https://[2001:db8::1]/", true)]
+ [DataRow("http://[2001:0DB8::1]", true, "http://[2001:db8::1]/", true)]
+ [DataRow("[2001:0DB8::1]:80", true, "https://[2001:db8::1]/", true)]
+ [DataRow("http://[2001:0DB8::1]:80", true, "http://[2001:db8::1]/", true)]
+ [DataRow("mailto:example@mail.com", true, "mailto:example@mail.com", false)]
+ [DataRow("tel:411", true, "tel:411", false)]
+ [DataRow("ftp://example.com", true, "ftp://example.com/", false)]
- public void TryParseCanParseHostName(string query, bool expectedSuccess, string expectedResult)
+ // This has been parsed as an application URI. Linked issue: #14260
+ [DataRow("example.com:443", true, "example.com:443", false)]
+ [DataRow("mailto:", true, "mailto:", false)]
+ [DataRow("mailto:/", false, null, false)]
+ [DataRow("ms-settings:", true, "ms-settings:", false)]
+ [DataRow("ms-settings:/", false, null, false)]
+ [DataRow("ms-settings://", false, null, false)]
+ [DataRow("ms-settings://privacy", true, "ms-settings://privacy/", false)]
+ [DataRow("ms-settings://privacy/", true, "ms-settings://privacy/", false)]
+ [DataRow("ms-settings:privacy", true, "ms-settings:privacy", false)]
+ [DataRow("ms-settings:powersleep", true, "ms-settings:powersleep", false)]
+ [DataRow("microsoft-edge:http://google.com", true, "microsoft-edge:http://google.com", false)]
+ [DataRow("microsoft-edge:https://google.com", true, "microsoft-edge:https://google.com", false)]
+ [DataRow("microsoft-edge:google.com", true, "microsoft-edge:google.com", false)]
+ [DataRow("microsoft-edge:google.com/", true, "microsoft-edge:google.com/", false)]
+ [DataRow("microsoft-edge:https://google.com/", true, "microsoft-edge:https://google.com/", false)]
+ [DataRow("ftp://user:password@localhost:8080", true, "ftp://user:password@localhost:8080/", false)]
+ [DataRow("ftp://user:password@localhost:8080/", true, "ftp://user:password@localhost:8080/", false)]
+ [DataRow("ftp://user:password@google.com", true, "ftp://user:password@google.com/", false)]
+ [DataRow("ftp://user:password@google.com:2121", true, "ftp://user:password@google.com:2121/", false)]
+ [DataRow("ftp://user:password@1.1.1.1", true, "ftp://user:password@1.1.1.1/", false)]
+ [DataRow("ftp://user:password@1.1.1.1:2121", true, "ftp://user:password@1.1.1.1:2121/", false)]
+
+ public void TryParseCanParseHostName(string query, bool expectedSuccess, string expectedResult, bool expectedIsWebUri)
{
// Arrange
var parser = new ExtendedUriParser();
// Act
- var success = parser.TryParse(query, out var result);
+ var success = parser.TryParse(query, out var result, out var isWebUriResult);
// Assert
Assert.AreEqual(expectedResult, result?.ToString());
+ Assert.AreEqual(expectedIsWebUri, isWebUriResult);
Assert.AreEqual(expectedSuccess, success);
}
}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Images/uri.dark.png b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Images/uri.dark.png
index 6c34ed1582..e080717309 100644
Binary files a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Images/uri.dark.png and b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Images/uri.dark.png differ
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Images/uri.light.png b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Images/uri.light.png
index bea54d005c..26722696aa 100644
Binary files a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Images/uri.light.png and b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Images/uri.light.png differ
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Interfaces/IUriParser.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Interfaces/IUriParser.cs
index aa0154bc35..bb72ae3241 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Interfaces/IUriParser.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Interfaces/IUriParser.cs
@@ -6,6 +6,6 @@ namespace Microsoft.Plugin.Uri.Interfaces
{
public interface IUriParser
{
- bool TryParse(string input, out System.Uri result);
+ bool TryParse(string input, out System.Uri result, out bool isWebUri);
}
}
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Main.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Main.cs
index a1ea328fa5..bea9768a96 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Main.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Main.cs
@@ -63,17 +63,15 @@ namespace Microsoft.Plugin.Uri
{
results.Add(new Result
{
- Title = Properties.Resources.Microsoft_plugin_uri_default_browser,
+ Title = Properties.Resources.Microsoft_plugin_uri_open,
SubTitle = BrowserPath,
- IcoPath = _uriSettings.ShowBrowserIcon
- ? BrowserIconPath
- : DefaultIconPath,
+ IcoPath = DefaultIconPath,
Action = action =>
{
if (!Helper.OpenInShell(BrowserPath))
{
var title = $"Plugin: {Properties.Resources.Microsoft_plugin_uri_plugin_name}";
- var message = $"{Properties.Resources.Microsoft_plugin_default_browser_open_failed}: ";
+ var message = $"{Properties.Resources.Microsoft_plugin_uri_open_failed}: ";
Context.API.ShowMsg(title, message);
return false;
}
@@ -85,16 +83,19 @@ namespace Microsoft.Plugin.Uri
}
if (!string.IsNullOrEmpty(query?.Search)
- && _uriParser.TryParse(query.Search, out var uriResult)
+ && _uriParser.TryParse(query.Search, out var uriResult, out var isWebUri)
&& _uriResolver.IsValidHost(uriResult))
{
var uriResultString = uriResult.ToString();
+ var isWebUriBool = isWebUri;
results.Add(new Result
{
Title = uriResultString,
- SubTitle = Properties.Resources.Microsoft_plugin_uri_website,
- IcoPath = _uriSettings.ShowBrowserIcon
+ SubTitle = isWebUriBool
+ ? Properties.Resources.Microsoft_plugin_uri_website
+ : Properties.Resources.Microsoft_plugin_uri_open,
+ IcoPath = isWebUriBool
? BrowserIconPath
: DefaultIconPath,
Action = action =>
@@ -118,7 +119,7 @@ namespace Microsoft.Plugin.Uri
private static bool IsActivationKeyword(Query query)
{
return !string.IsNullOrEmpty(query?.ActionKeyword)
- && query?.ActionKeyword == query?.RawQuery;
+ && query?.ActionKeyword == query?.RawQuery;
}
private bool IsDefaultBrowserSet()
@@ -155,16 +156,23 @@ namespace Microsoft.Plugin.Uri
UpdateBrowserIconPath(newTheme);
}
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "We want to keep the process alive but will log the exception")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage(
+ "Design",
+ "CA1031:Do not catch general exception types",
+ Justification = "We want to keep the process alive but will log the exception")]
private void UpdateBrowserIconPath(Theme newTheme)
{
try
{
- var progId = _registryWrapper.GetRegistryValue("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice", "ProgId");
+ var progId = _registryWrapper.GetRegistryValue(
+ "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
+ "ProgId");
var programLocation =
// Resolve App Icon (UWP)
- _registryWrapper.GetRegistryValue("HKEY_CLASSES_ROOT\\" + progId + "\\Application", "ApplicationIcon")
+ _registryWrapper.GetRegistryValue(
+ "HKEY_CLASSES_ROOT\\" + progId + "\\Application",
+ "ApplicationIcon")
// Resolves default file association icon (UWP + Normal)
?? _registryWrapper.GetRegistryValue("HKEY_CLASSES_ROOT\\" + progId + "\\DefaultIcon", null);
@@ -174,14 +182,21 @@ namespace Microsoft.Plugin.Uri
if (programLocation.StartsWith("@", StringComparison.Ordinal))
{
var directProgramLocationStringBuilder = new StringBuilder(128);
- if (NativeMethods.SHLoadIndirectString(programLocation, directProgramLocationStringBuilder, (uint)directProgramLocationStringBuilder.Capacity, IntPtr.Zero) ==
+ if (NativeMethods.SHLoadIndirectString(
+ programLocation,
+ directProgramLocationStringBuilder,
+ (uint)directProgramLocationStringBuilder.Capacity,
+ IntPtr.Zero) ==
NativeMethods.Hresult.Ok)
{
// Check if there's a postfix with contract-white/contrast-black icon is available and use that instead
var directProgramLocation = directProgramLocationStringBuilder.ToString();
- var themeIcon = newTheme == Theme.Light || newTheme == Theme.HighContrastWhite ? "contrast-white" : "contrast-black";
+ var themeIcon = newTheme == Theme.Light || newTheme == Theme.HighContrastWhite
+ ? "contrast-white"
+ : "contrast-black";
var extension = Path.GetExtension(directProgramLocation);
- var themedProgLocation = $"{directProgramLocation.Substring(0, directProgramLocation.Length - extension.Length)}_{themeIcon}{extension}";
+ var themedProgLocation =
+ $"{directProgramLocation.Substring(0, directProgramLocation.Length - extension.Length)}_{themeIcon}{extension}";
BrowserIconPath = File.Exists(themedProgLocation)
? themedProgLocation
: directProgramLocation;
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Properties/Resources.Designer.cs
index 71c6832a11..0d073767a1 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Properties/Resources.Designer.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Properties/Resources.Designer.cs
@@ -70,7 +70,7 @@ namespace Microsoft.Plugin.Uri.Properties {
}
///
- /// Looks up a localized string similar to Open Default Browser.
+ /// Looks up a localized string similar to Open default browser.
///
public static string Microsoft_plugin_uri_default_browser {
get {
@@ -79,7 +79,16 @@ namespace Microsoft.Plugin.Uri.Properties {
}
///
- /// Looks up a localized string similar to Failed to open URL.
+ /// Looks up a localized string similar to Open URI.
+ ///
+ public static string Microsoft_plugin_uri_open {
+ get {
+ return ResourceManager.GetString("Microsoft_plugin_uri_open", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Failed to open URI.
///
public static string Microsoft_plugin_uri_open_failed {
get {
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Properties/Resources.resx
index 6ce700ddef..19c0a39031 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Properties/Resources.resx
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Properties/Resources.resx
@@ -123,8 +123,11 @@
Open default browser
+
+ Open URI
+
- Failed to open URL
+ Failed to open URI
Opens URLs and UNC network shares.
@@ -135,4 +138,4 @@
Open in default browser
-
+
\ No newline at end of file
diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/UriHelper/ExtendedUriParser.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/UriHelper/ExtendedUriParser.cs
index 322b2a5efa..82d2327502 100644
--- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/UriHelper/ExtendedUriParser.cs
+++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/UriHelper/ExtendedUriParser.cs
@@ -10,22 +10,37 @@ namespace Microsoft.Plugin.Uri.UriHelper
{
public class ExtendedUriParser : IUriParser
{
- public bool TryParse(string input, out System.Uri result)
+ public bool TryParse(string input, out System.Uri result, out bool isWebUri)
{
if (string.IsNullOrEmpty(input))
{
result = default;
+ isWebUri = false;
return false;
}
+ // Handling URL with only scheme, typically mailto or application uri.
+ // Do nothing, return the result without urlBuilder
+ if (input.EndsWith(":", StringComparison.OrdinalIgnoreCase)
+ && !input.StartsWith("http", StringComparison.OrdinalIgnoreCase)
+ && !input.Contains("/", StringComparison.OrdinalIgnoreCase)
+ && !input.All(char.IsDigit))
+ {
+ result = new System.Uri(input);
+ isWebUri = false;
+ return true;
+ }
+
// Handle common cases UriBuilder does not handle
// Using CurrentCulture since this is a user typed string
if (input.EndsWith(":", StringComparison.CurrentCulture)
|| input.EndsWith(".", StringComparison.CurrentCulture)
|| input.EndsWith(":/", StringComparison.CurrentCulture)
+ || input.EndsWith("://", StringComparison.CurrentCulture)
|| input.All(char.IsDigit))
{
result = default;
+ isWebUri = false;
return false;
}
@@ -35,27 +50,31 @@ namespace Microsoft.Plugin.Uri.UriHelper
var hadDefaultPort = urlBuilder.Uri.IsDefaultPort;
urlBuilder.Port = hadDefaultPort ? -1 : urlBuilder.Port;
- if (input.Contains("HTTP://", StringComparison.OrdinalIgnoreCase))
+ if (input.StartsWith("HTTP://", StringComparison.OrdinalIgnoreCase))
{
urlBuilder.Scheme = System.Uri.UriSchemeHttp;
+ isWebUri = true;
}
else if (input.Contains(":", StringComparison.OrdinalIgnoreCase) &&
- !input.Contains("http", StringComparison.OrdinalIgnoreCase) &&
+ !input.StartsWith("http", StringComparison.OrdinalIgnoreCase) &&
!input.Contains("[", StringComparison.OrdinalIgnoreCase))
{
// Do nothing, leave unchanged
+ isWebUri = false;
}
else
{
urlBuilder.Scheme = System.Uri.UriSchemeHttps;
+ isWebUri = true;
}
result = urlBuilder.Uri;
return true;
}
- catch (System.UriFormatException)
+ catch (UriFormatException)
{
result = default;
+ isWebUri = false;
return false;
}
}