Skip to content

Commit c1bb977

Browse files
[dotnet] Tolerate invalid UTF-16 strings in DevTools JSON response (#14972)
Co-authored-by: Nikolay Borisenko <[email protected]>
1 parent 8740f13 commit c1bb977

File tree

4 files changed

+99
-3
lines changed

4 files changed

+99
-3
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// <copyright file="DevToolsJsonOptions.cs" company="Selenium Committers">
2+
// Licensed to the Software Freedom Conservancy (SFC) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The SFC licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
// </copyright>
19+
20+
using System.Text.Json;
21+
22+
#nullable enable
23+
24+
namespace OpenQA.Selenium.DevTools.Json;
25+
26+
internal static class DevToolsJsonOptions
27+
{
28+
public static JsonSerializerOptions Default { get; } = new JsonSerializerOptions()
29+
{
30+
Converters =
31+
{
32+
new StringConverter(),
33+
}
34+
};
35+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// <copyright file="StringConverter.cs" company="Selenium Committers">
2+
// Licensed to the Software Freedom Conservancy (SFC) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The SFC licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
// </copyright>
19+
20+
using System;
21+
using System.Text;
22+
using System.Text.Json;
23+
using System.Text.Json.Serialization;
24+
25+
#nullable enable
26+
27+
namespace OpenQA.Selenium.DevTools.Json;
28+
29+
internal sealed class StringConverter : JsonConverter<string>
30+
{
31+
public override bool HandleNull => true;
32+
33+
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
34+
{
35+
try
36+
{
37+
return reader.GetString();
38+
}
39+
catch (InvalidOperationException)
40+
{
41+
// Fallback to read the value as bytes instead of string.
42+
// System.Text.Json library throws exception when CDP remote end sends non-encoded string as binary data.
43+
// Using JavaScriptEncoder.UnsafeRelaxedJsonEscaping doesn't help because the string actually is byte[].
44+
// https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-Request - here "postData" property
45+
// is a string, which we cannot deserialize properly. This property is marked as deprecated, and new "postDataEntries"
46+
// is suggested for using, where most likely it is base64 encoded.
47+
48+
var bytes = reader.ValueSpan;
49+
var sb = new StringBuilder(bytes.Length);
50+
foreach (byte b in bytes)
51+
{
52+
sb.Append(Convert.ToChar(b));
53+
}
54+
55+
return sb.ToString();
56+
}
57+
}
58+
59+
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) =>
60+
writer.WriteStringValue(value);
61+
}

dotnet/src/webdriver/cdp/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ add an entry for version `<N>` to the `SupportedDevToolsVersions` dictionary ini
1717
6. In [`//dotnet/src/webdriver:WebDriver.csproj.prebuild.cmd`](https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/src/webdriver/WebDriver.csproj.prebuild.cmd),
1818
add the following block (substituting the proper value for `<N>`):
1919

20-
```
20+
```bash
2121
if not exist "%1..\..\..\bazel-bin\dotnet\src\webdriver\cdp\v<N>\DevToolsSessionDomains.cs" (
2222
echo Generating CDP code for version <N>
2323
pushd "%1..\..\.."
@@ -29,7 +29,7 @@ if not exist "%1..\..\..\bazel-bin\dotnet\src\webdriver\cdp\v<N>\DevToolsSessio
2929
7. In [`//dotnet/src/webdriver:WebDriver.csproj.prebuild.sh`](https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/src/webdriver/WebDriver.csproj.prebuild.sh),
3030
add the following block (substituting the proper value for `<N>`):
3131
32-
```
32+
```bash
3333
if [[ ! -f "$1../../../bazel-bin/dotnet/src/webdriver/cdp/v<N>/DevToolsSessionDomains.cs" ]]
3434
then
3535
echo "Generating CDP code for version <N>"

third_party/dotnet/devtools/src/generator/Templates/domain.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ namespace {{rootNamespace}}.{{domain.Name}}
6161
if (m_eventMap.ContainsKey(e.EventName))
6262
{
6363
var eventData = m_eventMap[e.EventName];
64-
var eventArgs = e.EventData.Deserialize(eventData.EventArgsType);
64+
var eventArgs = e.EventData.Deserialize(eventData.EventArgsType, global::OpenQA.Selenium.DevTools.Json.DevToolsJsonOptions.Default);
6565
eventData.EventInvoker(eventArgs);
6666
}
6767
}

0 commit comments

Comments
 (0)