mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
[PTRun][URL]Fix web link with ports support (#19809)
* [PT Run] Fix web link with ports support (#14260) * URL in the format of `domain:port` now directs to default browser * Add tests to verify web link with ports scenario * Fix test case and scenario where mismatching schema and port for IPv6 does not result in correct output * [PT Run][Tests] Change and add more UriParser Tests * Specifically of note is line 56, where [IPv6]:80 diverts to https instead of http. * [PT Run][Tests] Add UriParser tests * Add more tests targeting port handling * [PT Run] Fix http handling * This also fixes oddity with IPv4 and IPv6 handling * [PT Run] Add second results depending on condition * Test: update all test to reflect updated functions & add a little more tests * Update function to show two results when URI is in the format of `domain:port` (situation where it can also be `schema:path`) * Update regex style to follow previous code * [PT Run] Change tests and filter localhost from certain results * Add tests for 127.0.0.1, localhost, and ::1 * Move test around into more logical arrangement * Filter localhost out from showing double results * [PT Run] Fix spelling on comments * [PT Run] Add some words to expect.txt * [PT Toys] Clarify comment regarding [::] Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> * [PT Run] Remove tests regarding tel protocol * [PT Run] Clarify UriParser parameter * [PT Run] Add UriParser tests for tel protocol * Current code has a regression bug where tel:xxxx, if xxxx is more than 65536 it will break. Will fix in follow up commit. * [PT Run] Refactor ExtendedUriParser and its tests * Remove `isWebUri` from ExtendedUriParser, keeping only webUri and systemUri * Tel protocol regression bug still exists * [PT Run] Fix wrong icon when webUri result * [PT Run] Fix regression bug for tel protocol * Tel protocol will sometimes bug out when tel:xxxx if xxxxx is more than 65535, as UriBuilder will throw error thinking the port number has been exceeded * [PT Toy] Fix tel test * [PT Run] Changes to tests * Add test for application uri to include ports, for all non-protocol, http and https variants * Rearrange some more test to make more logical sense, and add comments * [PT Run] Simplify code * Move webUri and systemUri to be global, as per htcfreek's recommendation * Add comment to empty catch * Change null to default * [PT Toy] Update test name Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> * [PT Toy] Change result prompt when empty string * [PT Toy] Fix typo in comment * [PT Toy] Simplify line * [PT Toy] Change result prompt when empty string
This commit is contained in:
10
.github/actions/spell-check/expect.txt
vendored
10
.github/actions/spell-check/expect.txt
vendored
@@ -5,7 +5,6 @@ abgr
|
|||||||
abi
|
abi
|
||||||
ABlocked
|
ABlocked
|
||||||
ABOUTBOX
|
ABOUTBOX
|
||||||
ABradley
|
|
||||||
Abug
|
Abug
|
||||||
accctrl
|
accctrl
|
||||||
Acceleratorkeys
|
Acceleratorkeys
|
||||||
@@ -134,7 +133,6 @@ awakeness
|
|||||||
awakeversion
|
awakeversion
|
||||||
AWAYMODE
|
AWAYMODE
|
||||||
AYUV
|
AYUV
|
||||||
azchohfi
|
|
||||||
backend
|
backend
|
||||||
backtracer
|
backtracer
|
||||||
BADD
|
BADD
|
||||||
@@ -371,6 +369,7 @@ cxfksword
|
|||||||
CXSMICON
|
CXSMICON
|
||||||
CXVIRTUALSCREEN
|
CXVIRTUALSCREEN
|
||||||
cxxopts
|
cxxopts
|
||||||
|
Cxxx
|
||||||
cyberrex
|
cyberrex
|
||||||
Cyrl
|
Cyrl
|
||||||
CYSMICON
|
CYSMICON
|
||||||
@@ -420,7 +419,6 @@ Delimarsky
|
|||||||
dend
|
dend
|
||||||
DENORMAL
|
DENORMAL
|
||||||
Deondre
|
Deondre
|
||||||
dependabot
|
|
||||||
depersist
|
depersist
|
||||||
deprioritized
|
deprioritized
|
||||||
deps
|
deps
|
||||||
@@ -520,8 +518,8 @@ editshortcutswindow
|
|||||||
EFile
|
EFile
|
||||||
ekus
|
ekus
|
||||||
elif
|
elif
|
||||||
eltociear
|
|
||||||
elseif
|
elseif
|
||||||
|
eltociear
|
||||||
Emoji
|
Emoji
|
||||||
emptyrecyclebin
|
emptyrecyclebin
|
||||||
ENABLEDPOPUP
|
ENABLEDPOPUP
|
||||||
@@ -1093,6 +1091,7 @@ LOADSTRING
|
|||||||
LOBYTE
|
LOBYTE
|
||||||
LOCALAPPDATA
|
LOCALAPPDATA
|
||||||
LOCALDISPLAY
|
LOCALDISPLAY
|
||||||
|
localhost
|
||||||
LOCALPACKAGE
|
LOCALPACKAGE
|
||||||
localport
|
localport
|
||||||
localtime
|
localtime
|
||||||
@@ -1298,7 +1297,6 @@ NAMECHANGE
|
|||||||
nameof
|
nameof
|
||||||
namespace
|
namespace
|
||||||
Navassa
|
Navassa
|
||||||
naveensrinivasan
|
|
||||||
NCACTIVATE
|
NCACTIVATE
|
||||||
ncc
|
ncc
|
||||||
NCCALCSIZE
|
NCCALCSIZE
|
||||||
@@ -1440,8 +1438,8 @@ openxmlformats
|
|||||||
OPTIMIZEFORINVOKE
|
OPTIMIZEFORINVOKE
|
||||||
ORAW
|
ORAW
|
||||||
ORPHANEDDIALOGTITLE
|
ORPHANEDDIALOGTITLE
|
||||||
oss
|
|
||||||
osfanbuff
|
osfanbuff
|
||||||
|
oss
|
||||||
ostr
|
ostr
|
||||||
ostream
|
ostream
|
||||||
ostringstream
|
ostringstream
|
||||||
|
|||||||
@@ -11,86 +11,171 @@ namespace Microsoft.Plugin.Uri.UnitTests.UriHelper
|
|||||||
public class ExtendedUriParserTests
|
public class ExtendedUriParserTests
|
||||||
{
|
{
|
||||||
[DataTestMethod]
|
[DataTestMethod]
|
||||||
[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)]
|
|
||||||
|
|
||||||
// This has been parsed as an application URI. Linked issue: #14260
|
// Standard web uri
|
||||||
[DataRow("example.com:443", true, "example.com:443", false)]
|
[DataRow("google.com", true, "https://google.com/", null)]
|
||||||
[DataRow("mailto:", true, "mailto:", false)]
|
[DataRow("http://google.com", true, "http://google.com/", null)]
|
||||||
[DataRow("mailto:/", false, null, false)]
|
[DataRow("https://google.com", true, "https://google.com/", null)]
|
||||||
[DataRow("ms-settings:", true, "ms-settings:", false)]
|
[DataRow("ftps://google.com", true, null, "ftps://google.com/")]
|
||||||
[DataRow("ms-settings:/", false, null, false)]
|
[DataRow("bing.com/search?q=gmx", true, "https://bing.com/search?q=gmx", null)]
|
||||||
[DataRow("ms-settings://", false, null, false)]
|
[DataRow("http://bing.com/search?q=gmx", true, "http://bing.com/search?q=gmx", null)]
|
||||||
[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)]
|
|
||||||
[DataRow("^:", false, null, false)]
|
|
||||||
|
|
||||||
public void TryParseCanParseHostName(string query, bool expectedSuccess, string expectedResult, bool expectedIsWebUri)
|
// Edge cases
|
||||||
|
[DataRow("127", false, null, null)]
|
||||||
|
[DataRow(null, false, null, null)]
|
||||||
|
[DataRow("h", true, "https://h/", null)]
|
||||||
|
[DataRow("ht", true, "https://ht/", null)]
|
||||||
|
[DataRow("htt", true, "https://htt/", null)]
|
||||||
|
[DataRow("http", true, "https://http/", null)]
|
||||||
|
[DataRow("http:", false, null, null)]
|
||||||
|
[DataRow("http:/", false, null, null)]
|
||||||
|
[DataRow("http://", false, null, null)]
|
||||||
|
[DataRow("http://h", true, "http://h/", null)]
|
||||||
|
[DataRow("http://ht", true, "http://ht/", null)]
|
||||||
|
[DataRow("http://htt", true, "http://htt/", null)]
|
||||||
|
[DataRow("http://http", true, "http://http/", null)]
|
||||||
|
[DataRow("http://t", true, "http://t/", null)]
|
||||||
|
[DataRow("http://te", true, "http://te/", null)]
|
||||||
|
[DataRow("http://tes", true, "http://tes/", null)]
|
||||||
|
[DataRow("http://test", true, "http://test/", null)]
|
||||||
|
[DataRow("http://test.", false, null, null)]
|
||||||
|
[DataRow("http://test.c", true, "http://test.c/", null)]
|
||||||
|
[DataRow("http://test.co", true, "http://test.co/", null)]
|
||||||
|
[DataRow("http://test.com", true, "http://test.com/", null)]
|
||||||
|
[DataRow("http:3", true, "https://http:3/", null)]
|
||||||
|
[DataRow("http://http:3", true, "http://http:3/", null)]
|
||||||
|
[DataRow("[2001:0DB8::1]", true, "https://[2001:db8::1]/", null)]
|
||||||
|
[DataRow("[2001:0DB8::1]:80", true, "http://[2001:db8::1]/", null)]
|
||||||
|
[DataRow("[2001:0DB8::1]:443", true, "https://[2001:db8::1]/", null)]
|
||||||
|
[DataRow("http://[2001:0DB8::1]", true, "http://[2001:db8::1]/", null)]
|
||||||
|
[DataRow("http://[2001:0DB8::1]:80", true, "http://[2001:db8::1]/", null)]
|
||||||
|
[DataRow("http://[2001:0DB8::1]:443", true, "http://[2001:db8::1]:443/", null)]
|
||||||
|
[DataRow("https://[2001:0DB8::1]:80", true, "https://[2001:db8::1]:80/", null)]
|
||||||
|
[DataRow("http://test.test.test.test:952", true, "http://test.test.test.test:952/", null)]
|
||||||
|
[DataRow("https://test.test.test.test:952", true, "https://test.test.test.test:952/", null)]
|
||||||
|
|
||||||
|
// ToDo: Block [::] address results in parser. This Address is unspecified per RFC 4291 and the results make no sense.
|
||||||
|
[DataRow("[::]", true, "https://[::]/", null)]
|
||||||
|
[DataRow("http://[::]", true, "http://[::]/", null)]
|
||||||
|
|
||||||
|
// localhost, 127.0.0.1, ::1 tests
|
||||||
|
[DataRow("localhost", true, "https://localhost/", null)]
|
||||||
|
[DataRow("localhost:80", true, "http://localhost/", null)]
|
||||||
|
[DataRow("localhost:443", true, "https://localhost/", null)]
|
||||||
|
[DataRow("localhost:1234", true, "https://localhost:1234/", null)]
|
||||||
|
[DataRow("localhost/test", true, "https://localhost/test", null)]
|
||||||
|
[DataRow("localhost:80/test", true, "http://localhost/test", null)]
|
||||||
|
[DataRow("localhost:443/test", true, "https://localhost/test", null)]
|
||||||
|
[DataRow("localhost:1234/test", true, "https://localhost:1234/test", null)]
|
||||||
|
[DataRow("http://localhost", true, "http://localhost/", null)]
|
||||||
|
[DataRow("http://localhost:1234", true, "http://localhost:1234/", null)]
|
||||||
|
[DataRow("https://localhost", true, "https://localhost/", null)]
|
||||||
|
[DataRow("https://localhost:1234", true, "https://localhost:1234/", null)]
|
||||||
|
[DataRow("http://localhost/test", true, "http://localhost/test", null)]
|
||||||
|
[DataRow("http://localhost:1234/test", true, "http://localhost:1234/test", null)]
|
||||||
|
[DataRow("https://localhost/test", true, "https://localhost/test", null)]
|
||||||
|
[DataRow("https://localhost:1234/test", true, "https://localhost:1234/test", null)]
|
||||||
|
[DataRow("127.0.0.1", true, "https://127.0.0.1/", null)]
|
||||||
|
[DataRow("127.0.0.1:80", true, "http://127.0.0.1/", null)]
|
||||||
|
[DataRow("127.0.0.1:443", true, "https://127.0.0.1/", null)]
|
||||||
|
[DataRow("127.0.0.1:1234", true, "https://127.0.0.1:1234/", null)]
|
||||||
|
[DataRow("127.0.0.1/test", true, "https://127.0.0.1/test", null)]
|
||||||
|
[DataRow("127.0.0.1:80/test", true, "http://127.0.0.1/test", null)]
|
||||||
|
[DataRow("127.0.0.1:443/test", true, "https://127.0.0.1/test", null)]
|
||||||
|
[DataRow("127.0.0.1:1234/test", true, "https://127.0.0.1:1234/test", null)]
|
||||||
|
[DataRow("http://127.0.0.1", true, "http://127.0.0.1/", null)]
|
||||||
|
[DataRow("http://127.0.0.1:1234", true, "http://127.0.0.1:1234/", null)]
|
||||||
|
[DataRow("https://127.0.0.1", true, "https://127.0.0.1/", null)]
|
||||||
|
[DataRow("https://127.0.0.1:1234", true, "https://127.0.0.1:1234/", null)]
|
||||||
|
[DataRow("http://127.0.0.1/test", true, "http://127.0.0.1/test", null)]
|
||||||
|
[DataRow("http://127.0.0.1:1234/test", true, "http://127.0.0.1:1234/test", null)]
|
||||||
|
[DataRow("https://127.0.0.1/test", true, "https://127.0.0.1/test", null)]
|
||||||
|
[DataRow("https://127.0.0.1:1234/test", true, "https://127.0.0.1:1234/test", null)]
|
||||||
|
[DataRow("[::1]", true, "https://[::1]/", null)]
|
||||||
|
[DataRow("[::1]:80", true, "http://[::1]/", null)]
|
||||||
|
[DataRow("[::1]:443", true, "https://[::1]/", null)]
|
||||||
|
[DataRow("[::1]:1234", true, "https://[::1]:1234/", null)]
|
||||||
|
[DataRow("[::1]/test", true, "https://[::1]/test", null)]
|
||||||
|
[DataRow("[::1]:80/test", true, "http://[::1]/test", null)]
|
||||||
|
[DataRow("[::1]:443/test", true, "https://[::1]/test", null)]
|
||||||
|
[DataRow("[::1]:1234/test", true, "https://[::1]:1234/test", null)]
|
||||||
|
[DataRow("http://[::1]", true, "http://[::1]/", null)]
|
||||||
|
[DataRow("http://[::1]:1234", true, "http://[::1]:1234/", null)]
|
||||||
|
[DataRow("https://[::1]", true, "https://[::1]/", null)]
|
||||||
|
[DataRow("https://[::1]:1234", true, "https://[::1]:1234/", null)]
|
||||||
|
[DataRow("http://[::1]/test", true, "http://[::1]/test", null)]
|
||||||
|
[DataRow("http://[::1]:1234/test", true, "http://[::1]:1234/test", null)]
|
||||||
|
[DataRow("https://[::1]/test", true, "https://[::1]/test", null)]
|
||||||
|
[DataRow("https://[::1]:1234/test", true, "https://[::1]:1234/test", null)]
|
||||||
|
|
||||||
|
// Case where `domain:port`, as specified in issue #14260
|
||||||
|
// Assumption: Only domain with dot is accepted as sole webUri
|
||||||
|
[DataRow("example.com:80", true, "http://example.com/", null)]
|
||||||
|
[DataRow("example.com:80/test", true, "http://example.com/test", null)]
|
||||||
|
[DataRow("example.com:80/126", true, "http://example.com/126", null)]
|
||||||
|
[DataRow("example.com:443", true, "https://example.com/", null)]
|
||||||
|
[DataRow("example.com:443/test", true, "https://example.com/test", null)]
|
||||||
|
[DataRow("example.com:443/126", true, "https://example.com/126", null)]
|
||||||
|
[DataRow("google.com:91", true, "https://google.com:91/", null)]
|
||||||
|
[DataRow("google.com:91/test", true, "https://google.com:91/test", null)]
|
||||||
|
[DataRow("google.com:91/126", true, "https://google.com:91/126", null)]
|
||||||
|
[DataRow("test.test.test.test:952", true, "https://test.test.test.test:952/", null)]
|
||||||
|
[DataRow("test.test.test.test:952/test", true, "https://test.test.test.test:952/test", null)]
|
||||||
|
[DataRow("test.test.test.test:952/126", true, "https://test.test.test.test:952/126", null)]
|
||||||
|
|
||||||
|
// Following cases can be both interpreted as schema:path and domain:port
|
||||||
|
[DataRow("tel:411", true, "https://tel:411/", "tel:411")]
|
||||||
|
[DataRow("tel:70421567", true, null, "tel:70421567")]
|
||||||
|
[DataRow("tel:863-1234", true, null, "tel:863-1234")]
|
||||||
|
[DataRow("tel:+1-201-555-0123", true, null, "tel:+1-201-555-0123")]
|
||||||
|
|
||||||
|
// All following cases should be parsed as application URI
|
||||||
|
[DataRow("mailto:", true, null, "mailto:")]
|
||||||
|
[DataRow("mailto:/", false, null, null)]
|
||||||
|
[DataRow("mailto:example@mail.com", true, null, "mailto:example@mail.com")]
|
||||||
|
[DataRow("ms-settings:", true, null, "ms-settings:")]
|
||||||
|
[DataRow("ms-settings:/", false, null, null)]
|
||||||
|
[DataRow("ms-settings://", false, null, null)]
|
||||||
|
[DataRow("ms-settings://privacy", true, null, "ms-settings://privacy/")]
|
||||||
|
[DataRow("ms-settings://privacy/", true, null, "ms-settings://privacy/")]
|
||||||
|
[DataRow("ms-settings:privacy", true, null, "ms-settings:privacy")]
|
||||||
|
[DataRow("ms-settings:powersleep", true, null, "ms-settings:powersleep")]
|
||||||
|
[DataRow("microsoft-edge:google.com", true, null, "microsoft-edge:google.com")]
|
||||||
|
[DataRow("microsoft-edge:google.com/", true, null, "microsoft-edge:google.com/")]
|
||||||
|
[DataRow("microsoft-edge:google.com:80/", true, null, "microsoft-edge:google.com:80/")]
|
||||||
|
[DataRow("microsoft-edge:google.com:443/", true, null, "microsoft-edge:google.com:443/")]
|
||||||
|
[DataRow("microsoft-edge:google.com:1234/", true, null, "microsoft-edge:google.com:1234/")]
|
||||||
|
[DataRow("microsoft-edge:http://google.com", true, null, "microsoft-edge:http://google.com")]
|
||||||
|
[DataRow("microsoft-edge:http://google.com:80", true, null, "microsoft-edge:http://google.com:80")]
|
||||||
|
[DataRow("microsoft-edge:http://google.com:443", true, null, "microsoft-edge:http://google.com:443")]
|
||||||
|
[DataRow("microsoft-edge:http://google.com:1234", true, null, "microsoft-edge:http://google.com:1234")]
|
||||||
|
[DataRow("microsoft-edge:https://google.com", true, null, "microsoft-edge:https://google.com")]
|
||||||
|
[DataRow("microsoft-edge:https://google.com:80", true, null, "microsoft-edge:https://google.com:80")]
|
||||||
|
[DataRow("microsoft-edge:https://google.com:443", true, null, "microsoft-edge:https://google.com:443")]
|
||||||
|
[DataRow("microsoft-edge:https://google.com:1234", true, null, "microsoft-edge:https://google.com:1234")]
|
||||||
|
[DataRow("ftp://user:password@localhost:8080", true, null, "ftp://user:password@localhost:8080/")]
|
||||||
|
[DataRow("ftp://user:password@localhost:8080/", true, null, "ftp://user:password@localhost:8080/")]
|
||||||
|
[DataRow("ftp://user:password@google.com", true, null, "ftp://user:password@google.com/")]
|
||||||
|
[DataRow("ftp://user:password@google.com:2121", true, null, "ftp://user:password@google.com:2121/")]
|
||||||
|
[DataRow("ftp://user:password@1.1.1.1", true, null, "ftp://user:password@1.1.1.1/")]
|
||||||
|
[DataRow("ftp://user:password@1.1.1.1:2121", true, null, "ftp://user:password@1.1.1.1:2121/")]
|
||||||
|
[DataRow("ftp://example.com", true, null, "ftp://example.com/")]
|
||||||
|
[DataRow("ftp://example.com/test", true, null, "ftp://example.com/test")]
|
||||||
|
[DataRow("ftp://example.com:123/test", true, null, "ftp://example.com:123/test")]
|
||||||
|
[DataRow("ftp://example.com/126", true, null, "ftp://example.com/126")]
|
||||||
|
[DataRow("^:", false, null, null)]
|
||||||
|
|
||||||
|
public void ParserReturnsExpectedResults(string query, bool expectedSuccess, string expectedWebUri, string expectedSystemUri)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var parser = new ExtendedUriParser();
|
var parser = new ExtendedUriParser();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var success = parser.TryParse(query, out var result, out var isWebUriResult);
|
var success = parser.TryParse(query, out var webUriResult, out var systemUriResult);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.AreEqual(expectedResult, result?.ToString());
|
Assert.AreEqual(expectedWebUri, webUriResult?.ToString());
|
||||||
Assert.AreEqual(expectedIsWebUri, isWebUriResult);
|
Assert.AreEqual(expectedSystemUri, systemUriResult?.ToString());
|
||||||
Assert.AreEqual(expectedSuccess, success);
|
Assert.AreEqual(expectedSuccess, success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ namespace Microsoft.Plugin.Uri.Interfaces
|
|||||||
{
|
{
|
||||||
public interface IUriParser
|
public interface IUriParser
|
||||||
{
|
{
|
||||||
bool TryParse(string input, out System.Uri result, out bool isWebUri);
|
bool TryParse(string input, out System.Uri webUri, out System.Uri systemUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace Microsoft.Plugin.Uri
|
|||||||
{
|
{
|
||||||
results.Add(new Result
|
results.Add(new Result
|
||||||
{
|
{
|
||||||
Title = Properties.Resources.Microsoft_plugin_uri_open,
|
Title = Properties.Resources.Microsoft_plugin_uri_default_browser,
|
||||||
SubTitle = BrowserInfo.Path,
|
SubTitle = BrowserInfo.Path,
|
||||||
IcoPath = DefaultIconPath,
|
IcoPath = DefaultIconPath,
|
||||||
Action = action =>
|
Action = action =>
|
||||||
@@ -70,34 +70,54 @@ namespace Microsoft.Plugin.Uri
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(query?.Search)
|
if (!string.IsNullOrEmpty(query?.Search)
|
||||||
&& _uriParser.TryParse(query.Search, out var uriResult, out var isWebUri)
|
&& _uriParser.TryParse(query.Search, out var webUriResult, out var systemUriResult)
|
||||||
&& _uriResolver.IsValidHost(uriResult))
|
&& _uriResolver.IsValidHost(webUriResult))
|
||||||
{
|
{
|
||||||
var uriResultString = uriResult.ToString();
|
if (webUriResult is not null)
|
||||||
var isWebUriBool = isWebUri;
|
|
||||||
|
|
||||||
results.Add(new Result
|
|
||||||
{
|
{
|
||||||
Title = uriResultString,
|
var resultString = webUriResult.ToString();
|
||||||
SubTitle = isWebUriBool
|
results.Add(new Result
|
||||||
? Properties.Resources.Microsoft_plugin_uri_website
|
|
||||||
: Properties.Resources.Microsoft_plugin_uri_open,
|
|
||||||
IcoPath = isWebUriBool && BrowserInfo.IconPath != null
|
|
||||||
? BrowserInfo.IconPath
|
|
||||||
: DefaultIconPath,
|
|
||||||
Action = action =>
|
|
||||||
{
|
{
|
||||||
if (!Helper.OpenInShell(uriResultString))
|
Title = resultString,
|
||||||
|
SubTitle = Properties.Resources.Microsoft_plugin_uri_website,
|
||||||
|
IcoPath = BrowserInfo.IconPath,
|
||||||
|
Action = action =>
|
||||||
{
|
{
|
||||||
var title = $"Plugin: {Properties.Resources.Microsoft_plugin_uri_plugin_name}";
|
if (!Helper.OpenInShell(resultString))
|
||||||
var message = $"{Properties.Resources.Microsoft_plugin_uri_open_failed}: {uriResultString}";
|
{
|
||||||
Context.API.ShowMsg(title, message);
|
var title = $"Plugin: {Properties.Resources.Microsoft_plugin_uri_plugin_name}";
|
||||||
return false;
|
var message = $"{Properties.Resources.Microsoft_plugin_uri_open_failed}: {resultString}";
|
||||||
}
|
Context.API.ShowMsg(title, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (systemUriResult is not null)
|
||||||
|
{
|
||||||
|
var resultString = systemUriResult.ToString();
|
||||||
|
results.Add(new Result
|
||||||
|
{
|
||||||
|
Title = resultString,
|
||||||
|
SubTitle = Properties.Resources.Microsoft_plugin_uri_open,
|
||||||
|
IcoPath = DefaultIconPath,
|
||||||
|
Action = action =>
|
||||||
|
{
|
||||||
|
if (!Helper.OpenInShell(resultString))
|
||||||
|
{
|
||||||
|
var title = $"Plugin: {Properties.Resources.Microsoft_plugin_uri_plugin_name}";
|
||||||
|
var message = $"{Properties.Resources.Microsoft_plugin_uri_open_failed}: {resultString}";
|
||||||
|
Context.API.ShowMsg(title, message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
|||||||
@@ -12,18 +12,19 @@ namespace Microsoft.Plugin.Uri.UriHelper
|
|||||||
public class ExtendedUriParser : IUriParser
|
public class ExtendedUriParser : IUriParser
|
||||||
{
|
{
|
||||||
// When updating this method, also update the local method IsUri() in Community.PowerToys.Run.Plugin.WebSearch.Main.Query
|
// When updating this method, also update the local method IsUri() in Community.PowerToys.Run.Plugin.WebSearch.Main.Query
|
||||||
public bool TryParse(string input, out System.Uri result, out bool isWebUri)
|
public bool TryParse(string input, out System.Uri webUri, out System.Uri systemUri)
|
||||||
{
|
{
|
||||||
|
webUri = default;
|
||||||
|
systemUri = default;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(input))
|
if (string.IsNullOrEmpty(input))
|
||||||
{
|
{
|
||||||
result = default;
|
|
||||||
isWebUri = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handling URL with only scheme, typically mailto or application uri.
|
// Handling URL with only scheme, typically mailto or application uri.
|
||||||
// Do nothing, return the result without urlBuilder
|
// Do nothing, return the result without urlBuilder
|
||||||
// And check if scheme match REC3986 (issue #15035)
|
// And check if scheme match RFC3986 (issue #15035)
|
||||||
const string schemeRegex = @"^([a-z][a-z0-9+\-.]*):";
|
const string schemeRegex = @"^([a-z][a-z0-9+\-.]*):";
|
||||||
if (input.EndsWith(":", StringComparison.OrdinalIgnoreCase)
|
if (input.EndsWith(":", StringComparison.OrdinalIgnoreCase)
|
||||||
&& !input.StartsWith("http", StringComparison.OrdinalIgnoreCase)
|
&& !input.StartsWith("http", StringComparison.OrdinalIgnoreCase)
|
||||||
@@ -31,8 +32,7 @@ namespace Microsoft.Plugin.Uri.UriHelper
|
|||||||
&& !input.All(char.IsDigit)
|
&& !input.All(char.IsDigit)
|
||||||
&& Regex.IsMatch(input, schemeRegex))
|
&& Regex.IsMatch(input, schemeRegex))
|
||||||
{
|
{
|
||||||
result = new System.Uri(input);
|
systemUri = new System.Uri(input);
|
||||||
isWebUri = false;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,42 +44,66 @@ namespace Microsoft.Plugin.Uri.UriHelper
|
|||||||
|| input.EndsWith("://", StringComparison.CurrentCulture)
|
|| input.EndsWith("://", StringComparison.CurrentCulture)
|
||||||
|| input.All(char.IsDigit))
|
|| input.All(char.IsDigit))
|
||||||
{
|
{
|
||||||
result = default;
|
|
||||||
isWebUri = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
string isDomainPortRegex = @"^[\w\.]+:\d+";
|
||||||
|
string isIPv6PortRegex = @"^\[([\w:]+:+)+[\w]+\]:\d+";
|
||||||
var urlBuilder = new UriBuilder(input);
|
var urlBuilder = new UriBuilder(input);
|
||||||
var hadDefaultPort = urlBuilder.Uri.IsDefaultPort;
|
urlBuilder.Port = urlBuilder.Uri.IsDefaultPort ? -1 : urlBuilder.Port;
|
||||||
urlBuilder.Port = hadDefaultPort ? -1 : urlBuilder.Port;
|
|
||||||
|
|
||||||
if (input.StartsWith("HTTP://", StringComparison.OrdinalIgnoreCase))
|
if (input.StartsWith("HTTP://", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
urlBuilder.Scheme = System.Uri.UriSchemeHttp;
|
urlBuilder.Scheme = System.Uri.UriSchemeHttp;
|
||||||
isWebUri = true;
|
}
|
||||||
|
else if (Regex.IsMatch(input, isDomainPortRegex) ||
|
||||||
|
Regex.IsMatch(input, isIPv6PortRegex))
|
||||||
|
{
|
||||||
|
var secondUrlBuilder = urlBuilder;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
urlBuilder = new UriBuilder("https://" + input);
|
||||||
|
|
||||||
|
if (urlBuilder.Port == 80)
|
||||||
|
{
|
||||||
|
urlBuilder.Scheme = System.Uri.UriSchemeHttp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (UriFormatException)
|
||||||
|
{
|
||||||
|
// This handles the situation in tel:xxxx and others
|
||||||
|
// When xxxx > 65535, it will throw UriFormatException
|
||||||
|
// The catch ensures it will at least still try to return a systemUri
|
||||||
|
}
|
||||||
|
|
||||||
|
string singleLabelRegex = @"[\.:]+|^http$|^https$|^localhost$";
|
||||||
|
systemUri = Regex.IsMatch(urlBuilder.Host, singleLabelRegex) ? null : secondUrlBuilder.Uri;
|
||||||
}
|
}
|
||||||
else if (input.Contains(':', StringComparison.OrdinalIgnoreCase) &&
|
else if (input.Contains(':', StringComparison.OrdinalIgnoreCase) &&
|
||||||
!input.StartsWith("http", StringComparison.OrdinalIgnoreCase) &&
|
!input.StartsWith("http", StringComparison.OrdinalIgnoreCase) &&
|
||||||
!input.Contains('[', StringComparison.OrdinalIgnoreCase))
|
!input.Contains('[', StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// Do nothing, leave unchanged
|
// Do nothing, leave unchanged
|
||||||
isWebUri = false;
|
systemUri = urlBuilder.Uri;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
urlBuilder.Scheme = System.Uri.UriSchemeHttps;
|
urlBuilder.Scheme = System.Uri.UriSchemeHttps;
|
||||||
isWebUri = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = urlBuilder.Uri;
|
if (urlBuilder.Scheme.Equals(System.Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
urlBuilder.Scheme.Equals(System.Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
webUri = urlBuilder.Uri;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (UriFormatException)
|
catch (UriFormatException)
|
||||||
{
|
{
|
||||||
result = default;
|
|
||||||
isWebUri = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user