Skip to content

Commit f15603f

Browse files
authored
Bidi: Console support (#3016)
* Bidi: Console support * update * fix * fix some tests * impovements * fix * fix a test * some improvements
1 parent de2ac96 commit f15603f

File tree

10 files changed

+445
-100
lines changed

10 files changed

+445
-100
lines changed

lib/PuppeteerSharp.Nunit/TestExpectations/TestExpectations.local.json

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -937,21 +937,6 @@
937937
"FAIL"
938938
]
939939
},
940-
{
941-
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
942-
"testIdPattern": "[page.spec] *Page.Events.Console*",
943-
"platforms": [
944-
"darwin",
945-
"linux",
946-
"win32"
947-
],
948-
"parameters": [
949-
"webDriverBiDi"
950-
],
951-
"expectations": [
952-
"FAIL"
953-
]
954-
},
955940
{
956941
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
957942
"testIdPattern": "[page.spec] *Page.Events.DOMContentLoaded*",
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>console.trace test</title>
5+
</head>
6+
<body>
7+
<script>
8+
function foo() {
9+
console.trace('yellow')
10+
}
11+
function bar() {
12+
foo();
13+
}
14+
bar();
15+
</script>
16+
</body>
17+
</html>

lib/PuppeteerSharp.Tests/PageTests/PageEventsConsoleTests.cs

Lines changed: 195 additions & 78 deletions
Large diffs are not rendered by default.

lib/PuppeteerSharp.Tests/PuppeteerPageBaseTest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Diagnostics;
2+
using System.Linq;
23
using System.Threading.Tasks;
34
using NUnit.Framework;
45

@@ -38,5 +39,24 @@ void ErrorEvent(object sender, ErrorEventArgs e)
3839

3940
return wrapper.Task;
4041
}
42+
43+
#pragma warning disable CS0618 // Type or member is obsolete
44+
protected async Task<ITarget> FindPopupTargetAsync(IPage excludePage)
45+
{
46+
var targets = excludePage.BrowserContext.Targets();
47+
var pageTargets = targets.Where(t => t.Type == TargetType.Page).ToArray();
48+
49+
foreach (var target in pageTargets)
50+
{
51+
var targetPage = await target.PageAsync();
52+
if (targetPage != null && targetPage != excludePage)
53+
{
54+
return target;
55+
}
56+
}
57+
58+
return null;
59+
}
60+
#pragma warning restore CS0618 // Type or member is obsolete
4161
}
4262
}

lib/PuppeteerSharp/Bidi/BidiDeserializer.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,25 @@ namespace PuppeteerSharp.Bidi;
2929

3030
internal static class BidiDeserializer
3131
{
32+
public static object Deserialize(RemoteValue value)
33+
{
34+
if (value == null)
35+
{
36+
return null;
37+
}
38+
39+
return value.Type switch
40+
{
41+
"undefined" => null,
42+
"null" => null,
43+
"string" => value.Value,
44+
"number" => DeserializeNumber(value.Value),
45+
"boolean" => value.Value,
46+
"bigint" => value.Value != null ? Convert.ToInt64(value.Value, CultureInfo.InvariantCulture) : null,
47+
_ => null,
48+
};
49+
}
50+
3251
public static object ToPrettyPrint(this RemoteValue value)
3352
{
3453
if (value is null)

lib/PuppeteerSharp/Bidi/BidiFrame.cs

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,65 @@ internal static BidiFrame From(BidiPage parentPage, BidiFrame parentFrame, Brows
396396
/// <inheritdoc />
397397
protected internal override DeviceRequestPromptManager GetDeviceRequestPromptManager() => throw new System.NotImplementedException();
398398

399+
private static ConsoleType ConvertConsoleMessageLevel(string method)
400+
{
401+
return method switch
402+
{
403+
"group" => ConsoleType.StartGroup,
404+
"groupCollapsed" => ConsoleType.StartGroupCollapsed,
405+
"groupEnd" => ConsoleType.EndGroup,
406+
"log" => ConsoleType.Log,
407+
"debug" => ConsoleType.Debug,
408+
"info" => ConsoleType.Info,
409+
"error" => ConsoleType.Error,
410+
"warn" => ConsoleType.Warning,
411+
"dir" => ConsoleType.Dir,
412+
"dirxml" => ConsoleType.Dirxml,
413+
"table" => ConsoleType.Table,
414+
"trace" => ConsoleType.Trace,
415+
"clear" => ConsoleType.Clear,
416+
"assert" => ConsoleType.Assert,
417+
"profile" => ConsoleType.Profile,
418+
"profileEnd" => ConsoleType.ProfileEnd,
419+
"count" => ConsoleType.Count,
420+
"timeEnd" => ConsoleType.TimeEnd,
421+
"verbose" => ConsoleType.Verbose,
422+
"timeStamp" => ConsoleType.Timestamp,
423+
_ => ConsoleType.Log,
424+
};
425+
}
426+
427+
private static ConsoleMessageLocation GetStackTraceLocation(WebDriverBiDi.Script.StackTrace stackTrace)
428+
{
429+
if (stackTrace?.CallFrames?.Count > 0)
430+
{
431+
var callFrame = stackTrace.CallFrames[0];
432+
return new ConsoleMessageLocation
433+
{
434+
URL = callFrame.Url,
435+
LineNumber = (int)callFrame.LineNumber,
436+
ColumnNumber = (int)callFrame.ColumnNumber,
437+
};
438+
}
439+
440+
return null;
441+
}
442+
443+
private static IList<ConsoleMessageLocation> GetStackTrace(WebDriverBiDi.Script.StackTrace stackTrace)
444+
{
445+
if (stackTrace?.CallFrames?.Count > 0)
446+
{
447+
return stackTrace.CallFrames.Select(callFrame => new ConsoleMessageLocation
448+
{
449+
URL = callFrame.Url,
450+
LineNumber = (int)callFrame.LineNumber,
451+
ColumnNumber = (int)callFrame.ColumnNumber,
452+
}).ToList();
453+
}
454+
455+
return [];
456+
}
457+
399458
private PuppeteerException RewriteNavigationError(Exception ex, string url, int timeoutSettingsNavigationTimeout)
400459
{
401460
return ex is TimeoutException
@@ -582,7 +641,41 @@ private void Initialize()
582641

583642
BrowsingContext.Log += (sender, args) =>
584643
{
585-
if (args.Type == "javascript")
644+
if (Id != args.Source.Context)
645+
{
646+
return;
647+
}
648+
649+
if (args.Type == "console")
650+
{
651+
var consoleArgs = args.Arguments;
652+
var handleArgs = consoleArgs?.Select(arg => ((BidiFrameRealm)MainRealm).CreateHandle(arg)).ToArray() ?? [];
653+
654+
var text = string.Join(
655+
" ",
656+
handleArgs.Select(arg =>
657+
{
658+
if (arg is BidiJSHandle { IsPrimitiveValue: true } jsHandle)
659+
{
660+
return BidiDeserializer.Deserialize(jsHandle.RemoteValue);
661+
}
662+
663+
return arg.ToString();
664+
})).Trim();
665+
666+
var location = GetStackTraceLocation(args.StackTrace);
667+
var stackTrace = GetStackTrace(args.StackTrace);
668+
669+
var consoleMessage = new ConsoleMessage(
670+
ConvertConsoleMessageLevel(args.Method),
671+
text,
672+
handleArgs,
673+
location,
674+
stackTrace);
675+
676+
BidiPage.OnConsole(new ConsoleEventArgs(consoleMessage));
677+
}
678+
else if (args.Type == "javascript")
586679
{
587680
var text = args.Text ?? string.Empty;
588681
var messageLines = new List<string> { text };

lib/PuppeteerSharp/Bidi/BidiPage.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ internal static BidiPage From(BidiBrowserContext browserContext, BrowsingContext
575575

576576
internal new void OnPageError(PageErrorEventArgs e) => base.OnPageError(e);
577577

578+
internal new void OnConsole(ConsoleEventArgs e) => base.OnConsole(e);
579+
578580
internal void OnWorkerCreated(BidiWebWorker worker)
579581
{
580582
_workers[worker.RealmId] = worker;

lib/PuppeteerSharp/Bidi/BidiRealm.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,18 @@ internal override async Task EvaluateFunctionAsync(string script, params object[
130130
await EvaluateAsync(true, false, script, args).ConfigureAwait(false);
131131
}
132132

133+
internal IJSHandle CreateHandle(RemoteValue remoteValue)
134+
{
135+
if (
136+
remoteValue.Type is "node" or "window" &&
137+
this is BidiFrameRealm)
138+
{
139+
return BidiElementHandle.From(remoteValue, this);
140+
}
141+
142+
return BidiJSHandle.From(remoteValue, this);
143+
}
144+
133145
protected virtual void ThrowIfDetached()
134146
{
135147
// Base implementation does nothing
@@ -415,6 +427,13 @@ private async Task<ArgumentValue> FormatArgumentAsync(object arg)
415427

416428
return LocalValue.Array(list.Select(el => el as LocalValue).ToList());
417429
case BidiJSHandle objectHandle:
430+
// If the handle doesn't have a valid handle ID (e.g., from console log args),
431+
// serialize its value directly instead of using it as a remote reference
432+
if (string.IsNullOrEmpty(objectHandle.Id))
433+
{
434+
return SerializeRemoteValue(objectHandle.RemoteValue);
435+
}
436+
418437
return objectHandle.RemoteValue.ToRemoteReference();
419438
case BidiElementHandle elementHandle:
420439
return elementHandle.Value.ToRemoteReference();
@@ -424,4 +443,62 @@ private async Task<ArgumentValue> FormatArgumentAsync(object arg)
424443

425444
return null;
426445
}
446+
447+
private LocalValue SerializeRemoteValue(RemoteValue remoteValue)
448+
{
449+
return remoteValue.Type switch
450+
{
451+
"undefined" => LocalValue.Undefined,
452+
"null" => LocalValue.Null,
453+
"string" => LocalValue.String((string)remoteValue.Value),
454+
"number" => remoteValue.Value switch
455+
{
456+
long l => LocalValue.Number(l),
457+
double d when double.IsPositiveInfinity(d) => LocalValue.Infinity,
458+
double d when double.IsNegativeInfinity(d) => LocalValue.NegativeInfinity,
459+
double d when double.IsNaN(d) => LocalValue.NaN,
460+
double d => LocalValue.Number(d),
461+
_ => LocalValue.Number(Convert.ToDouble(remoteValue.Value, CultureInfo.InvariantCulture)),
462+
},
463+
"boolean" => LocalValue.Boolean((bool)remoteValue.Value),
464+
"bigint" => LocalValue.BigInt(BigInteger.Parse((string)remoteValue.Value, CultureInfo.InvariantCulture)),
465+
"array" => SerializeRemoteValueArray(remoteValue.Value),
466+
"object" => SerializeRemoteValueObject(remoteValue.Value),
467+
_ => throw new PuppeteerException($"Cannot serialize RemoteValue of type '{remoteValue.Type}' without a handle"),
468+
};
469+
}
470+
471+
private LocalValue SerializeRemoteValueArray(object value)
472+
{
473+
var items = new List<LocalValue>();
474+
if (value is IEnumerable<RemoteValue> remoteValues)
475+
{
476+
foreach (var item in remoteValues)
477+
{
478+
items.Add(SerializeRemoteValue(item));
479+
}
480+
}
481+
482+
return LocalValue.Array(items);
483+
}
484+
485+
private LocalValue SerializeRemoteValueObject(object value)
486+
{
487+
var dict = new Dictionary<string, LocalValue>();
488+
if (value is RemoteValueDictionary remoteDict)
489+
{
490+
foreach (var kvp in remoteDict)
491+
{
492+
var key = kvp.Key switch
493+
{
494+
string s => s,
495+
_ => kvp.Key.ToString(),
496+
};
497+
var val = SerializeRemoteValue(kvp.Value);
498+
dict[key] = val;
499+
}
500+
}
501+
502+
return LocalValue.Object(dict);
503+
}
427504
}

lib/PuppeteerSharp/Cdp/CdpPage.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,15 +1127,23 @@ await Task.WhenAll(values.Select(v =>
11271127
: RemoteObjectHelper.ValueFromRemoteObject<object>(handle.RemoteObject)?.ToString() ?? "null";
11281128
});
11291129
var location = new ConsoleMessageLocation();
1130+
var stackTraceLocations = new List<ConsoleMessageLocation>();
11301131
if (stackTrace?.CallFrames?.Length > 0)
11311132
{
1132-
var callFrame = stackTrace.CallFrames[0];
1133-
location.URL = callFrame.URL;
1134-
location.LineNumber = callFrame.LineNumber;
1135-
location.ColumnNumber = callFrame.ColumnNumber;
1133+
foreach (var callFrame in stackTrace.CallFrames)
1134+
{
1135+
stackTraceLocations.Add(new ConsoleMessageLocation
1136+
{
1137+
URL = callFrame.URL,
1138+
LineNumber = callFrame.LineNumber,
1139+
ColumnNumber = callFrame.ColumnNumber,
1140+
});
1141+
}
1142+
1143+
location = stackTraceLocations[0];
11361144
}
11371145

1138-
var consoleMessage = new ConsoleMessage(type, string.Join(" ", tokens), values, location);
1146+
var consoleMessage = new ConsoleMessage(type, string.Join(" ", tokens), values, location, stackTraceLocations);
11391147
OnConsole(new ConsoleEventArgs(consoleMessage));
11401148
}
11411149

lib/PuppeteerSharp/ConsoleMessage.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ public class ConsoleMessage
1414
/// <param name="text">Text.</param>
1515
/// <param name="args">Arguments.</param>
1616
/// <param name="location">Message location.</param>
17-
public ConsoleMessage(ConsoleType type, string text, IList<IJSHandle> args, ConsoleMessageLocation location = null)
17+
/// <param name="stackTrace">Stack trace.</param>
18+
public ConsoleMessage(ConsoleType type, string text, IList<IJSHandle> args, ConsoleMessageLocation location = null, IList<ConsoleMessageLocation> stackTrace = null)
1819
{
1920
Type = type;
2021
Text = text;
2122
Args = args;
2223
Location = location;
24+
StackTrace = stackTrace ?? [];
2325
}
2426

2527
/// <summary>
@@ -44,5 +46,10 @@ public ConsoleMessage(ConsoleType type, string text, IList<IJSHandle> args, Cons
4446
/// Gets the location.
4547
/// </summary>
4648
public ConsoleMessageLocation Location { get; }
49+
50+
/// <summary>
51+
/// Gets the stack trace.
52+
/// </summary>
53+
public IList<ConsoleMessageLocation> StackTrace { get; }
4754
}
4855
}

0 commit comments

Comments
 (0)