Improve OSC 8 Hyperlink parsing logic (#7962)

This PR improves the OSC 8 Hyperlink parsing logic, by adding support to
`:` in params.

## Validation Steps Performed

Tests added & passed.
This commit is contained in:
Chester Liu 2020-12-03 08:33:29 +08:00 committed by GitHub
parent 60f1b0b285
commit 3181b6a517
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 8 deletions

View file

@ -884,11 +884,16 @@ try
}
CATCH_LOG_RETURN_FALSE()
#pragma warning(push)
#pragma warning(disable : 26445) // Suppress lifetime check for a reference to gsl::span or std::string_view
// Routine Description:
// - Given a hyperlink string, attempts to parse the URI encoded. An 'id' parameter
// may be provided.
// If there is a URI, the well formatted string looks like:
// "<params>;<URI>"
// To be specific, params is an optional list of key=value assignments, separated by the ':'. Example:
// "id=xyz123:foo=bar:baz=value"
// If there is no URI, we need to close the hyperlink and the string looks like:
// ";"
// Arguments:
@ -903,18 +908,24 @@ bool OutputStateMachineEngine::_ParseHyperlink(const std::wstring_view string,
{
params.clear();
uri.clear();
const auto len = string.size();
if (string == L";")
{
return true;
}
const size_t midPos = string.find(';');
if (midPos != std::wstring::npos)
{
if (len != 1)
uri = string.substr(midPos + 1);
const auto paramStr = string.substr(0, midPos);
const auto paramParts = Utils::SplitString(paramStr, ':');
for (const auto& part : paramParts)
{
uri = string.substr(midPos + 1);
const auto paramStr = string.substr(0, midPos);
const auto idPos = paramStr.find(hyperlinkIDParameter);
const auto idPos = part.find(hyperlinkIDParameter);
if (idPos != std::wstring::npos)
{
params = paramStr.substr(idPos + hyperlinkIDParameter.size());
params = part.substr(idPos + hyperlinkIDParameter.size());
}
}
return true;
@ -922,6 +933,8 @@ bool OutputStateMachineEngine::_ParseHyperlink(const std::wstring_view string,
return false;
}
#pragma warning(pop)
// Routine Description:
// - OSC 10, 11, 12 ; spec ST
// spec: The colors are specified by name or RGB specification as per XParseColor

View file

@ -3239,6 +3239,54 @@ class StateMachineExternalTest final
VERIFY_IS_FALSE(pDispatch->_hyperlinkMode);
VERIFY_IS_TRUE(pDispatch->_uri.empty());
// Let's try more complicated params and URLs
mach.ProcessString(L"\x1b]8;id=testId;https://example.com\x9c");
VERIFY_IS_TRUE(pDispatch->_hyperlinkMode);
VERIFY_ARE_EQUAL(pDispatch->_uri, L"https://example.com");
VERIFY_ARE_EQUAL(pDispatch->_customId, L"testId");
mach.ProcessString(L"\x1b]8;;\x9c");
VERIFY_IS_FALSE(pDispatch->_hyperlinkMode);
VERIFY_IS_TRUE(pDispatch->_uri.empty());
// Multiple params
mach.ProcessString(L"\x1b]8;id=testId:foo=bar;https://example.com\x9c");
VERIFY_IS_TRUE(pDispatch->_hyperlinkMode);
VERIFY_ARE_EQUAL(pDispatch->_uri, L"https://example.com");
VERIFY_ARE_EQUAL(pDispatch->_customId, L"testId");
mach.ProcessString(L"\x1b]8;;\x9c");
VERIFY_IS_FALSE(pDispatch->_hyperlinkMode);
VERIFY_IS_TRUE(pDispatch->_uri.empty());
mach.ProcessString(L"\x1b]8;foo=bar:id=testId;https://example.com\x9c");
VERIFY_IS_TRUE(pDispatch->_hyperlinkMode);
VERIFY_ARE_EQUAL(pDispatch->_uri, L"https://example.com");
VERIFY_ARE_EQUAL(pDispatch->_customId, L"testId");
mach.ProcessString(L"\x1b]8;;\x9c");
VERIFY_IS_FALSE(pDispatch->_hyperlinkMode);
VERIFY_IS_TRUE(pDispatch->_uri.empty());
// URIs with query strings
mach.ProcessString(L"\x1b]8;id=testId;https://example.com?query1=value1\x9c");
VERIFY_IS_TRUE(pDispatch->_hyperlinkMode);
VERIFY_ARE_EQUAL(pDispatch->_uri, L"https://example.com?query1=value1");
VERIFY_ARE_EQUAL(pDispatch->_customId, L"testId");
mach.ProcessString(L"\x1b]8;;\x9c");
VERIFY_IS_FALSE(pDispatch->_hyperlinkMode);
VERIFY_IS_TRUE(pDispatch->_uri.empty());
mach.ProcessString(L"\x1b]8;id=testId;https://example.com?query1=value1;value2;value3\x9c");
VERIFY_IS_TRUE(pDispatch->_hyperlinkMode);
VERIFY_ARE_EQUAL(pDispatch->_uri, L"https://example.com?query1=value1;value2;value3");
VERIFY_ARE_EQUAL(pDispatch->_customId, L"testId");
mach.ProcessString(L"\x1b]8;;\x9c");
VERIFY_IS_FALSE(pDispatch->_hyperlinkMode);
VERIFY_IS_TRUE(pDispatch->_uri.empty());
pDispatch->ClearState();
}
};

View file

@ -50,7 +50,7 @@ namespace Microsoft::Console::Utils
bool HexToUint(const wchar_t wch, unsigned int& value) noexcept;
bool StringToUint(const std::wstring_view wstr, unsigned int& value);
std::vector<std::wstring_view> SplitString(const std::wstring_view wstr, const wchar_t delimiter);
std::vector<std::wstring_view> SplitString(const std::wstring_view wstr, const wchar_t delimiter) noexcept;
constexpr uint16_t EndianSwap(uint16_t value)
{

View file

@ -104,6 +104,8 @@ void UtilsTests::TestSplitString()
VERIFY_ARE_EQUAL(0u, result.size());
result = SplitString(L"1", L';');
VERIFY_ARE_EQUAL(1u, result.size());
result = SplitString(L";", L';');
VERIFY_ARE_EQUAL(2u, result.size());
result = SplitString(L"123", L';');
VERIFY_ARE_EQUAL(1u, result.size());

View file

@ -391,7 +391,8 @@ bool Utils::StringToUint(const std::wstring_view wstr,
// Return Value:
// - a vector containing the result string parts.
std::vector<std::wstring_view> Utils::SplitString(const std::wstring_view wstr,
const wchar_t delimiter)
const wchar_t delimiter) noexcept
try
{
std::vector<std::wstring_view> result;
size_t current = 0;
@ -421,6 +422,11 @@ std::vector<std::wstring_view> Utils::SplitString(const std::wstring_view wstr,
return result;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
return {};
}
// Routine Description:
// - Shorthand check if a handle value is null or invalid.