Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -937,21 +937,6 @@
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[page.spec] *Page.Events.Console*",
"platforms": [
"darwin",
"linux",
"win32"
],
"parameters": [
"webDriverBiDi"
],
"expectations": [
"FAIL"
]
},
{
"comment": "This is part of organizing the webdriver bidi implementation, We will remove it one by one",
"testIdPattern": "[page.spec] *Page.Events.DOMContentLoaded*",
Expand Down
17 changes: 17 additions & 0 deletions lib/PuppeteerSharp.TestServer/wwwroot/consoletrace.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>console.trace test</title>
</head>
<body>
<script>
function foo() {
console.trace('yellow')
}
function bar() {
foo();
}
bar();
</script>
</body>
</html>
273 changes: 195 additions & 78 deletions lib/PuppeteerSharp.Tests/PageTests/PageEventsConsoleTests.cs

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions lib/PuppeteerSharp.Tests/PuppeteerPageBaseTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;

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

return wrapper.Task;
}

#pragma warning disable CS0618 // Type or member is obsolete
protected async Task<ITarget> FindPopupTargetAsync(IPage excludePage)
{
var targets = excludePage.BrowserContext.Targets();
var pageTargets = targets.Where(t => t.Type == TargetType.Page).ToArray();

foreach (var target in pageTargets)
{
var targetPage = await target.PageAsync();
if (targetPage != null && targetPage != excludePage)
{
return target;
}
}

return null;
}
#pragma warning restore CS0618 // Type or member is obsolete
}
}
19 changes: 19 additions & 0 deletions lib/PuppeteerSharp/Bidi/BidiDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,25 @@ namespace PuppeteerSharp.Bidi;

internal static class BidiDeserializer
{
public static object Deserialize(RemoteValue value)
{
if (value == null)
{
return null;
}

return value.Type switch
{
"undefined" => null,
"null" => null,
"string" => value.Value,
"number" => DeserializeNumber(value.Value),
"boolean" => value.Value,
"bigint" => value.Value != null ? Convert.ToInt64(value.Value, CultureInfo.InvariantCulture) : null,
_ => null,
};
}

public static object ToPrettyPrint(this RemoteValue value)
{
if (value is null)
Expand Down
95 changes: 94 additions & 1 deletion lib/PuppeteerSharp/Bidi/BidiFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,65 @@ internal static BidiFrame From(BidiPage parentPage, BidiFrame parentFrame, Brows
/// <inheritdoc />
protected internal override DeviceRequestPromptManager GetDeviceRequestPromptManager() => throw new System.NotImplementedException();

private static ConsoleType ConvertConsoleMessageLevel(string method)
{
return method switch
{
"group" => ConsoleType.StartGroup,
"groupCollapsed" => ConsoleType.StartGroupCollapsed,
"groupEnd" => ConsoleType.EndGroup,
"log" => ConsoleType.Log,
"debug" => ConsoleType.Debug,
"info" => ConsoleType.Info,
"error" => ConsoleType.Error,
"warn" => ConsoleType.Warning,
"dir" => ConsoleType.Dir,
"dirxml" => ConsoleType.Dirxml,
"table" => ConsoleType.Table,
"trace" => ConsoleType.Trace,
"clear" => ConsoleType.Clear,
"assert" => ConsoleType.Assert,
"profile" => ConsoleType.Profile,
"profileEnd" => ConsoleType.ProfileEnd,
"count" => ConsoleType.Count,
"timeEnd" => ConsoleType.TimeEnd,
"verbose" => ConsoleType.Verbose,
"timeStamp" => ConsoleType.Timestamp,
_ => ConsoleType.Log,
};
}

private static ConsoleMessageLocation GetStackTraceLocation(WebDriverBiDi.Script.StackTrace stackTrace)
{
if (stackTrace?.CallFrames?.Count > 0)
{
var callFrame = stackTrace.CallFrames[0];
return new ConsoleMessageLocation
{
URL = callFrame.Url,
LineNumber = (int)callFrame.LineNumber,
ColumnNumber = (int)callFrame.ColumnNumber,
};
}

return null;
}

private static IList<ConsoleMessageLocation> GetStackTrace(WebDriverBiDi.Script.StackTrace stackTrace)
{
if (stackTrace?.CallFrames?.Count > 0)
{
return stackTrace.CallFrames.Select(callFrame => new ConsoleMessageLocation
{
URL = callFrame.Url,
LineNumber = (int)callFrame.LineNumber,
ColumnNumber = (int)callFrame.ColumnNumber,
}).ToList();
}

return [];
}

private PuppeteerException RewriteNavigationError(Exception ex, string url, int timeoutSettingsNavigationTimeout)
{
return ex is TimeoutException
Expand Down Expand Up @@ -582,7 +641,41 @@ private void Initialize()

BrowsingContext.Log += (sender, args) =>
{
if (args.Type == "javascript")
if (Id != args.Source.Context)
{
return;
}

if (args.Type == "console")
{
var consoleArgs = args.Arguments;
var handleArgs = consoleArgs?.Select(arg => ((BidiFrameRealm)MainRealm).CreateHandle(arg)).ToArray() ?? [];

var text = string.Join(
" ",
handleArgs.Select(arg =>
{
if (arg is BidiJSHandle { IsPrimitiveValue: true } jsHandle)
{
return BidiDeserializer.Deserialize(jsHandle.RemoteValue);
}

return arg.ToString();
})).Trim();

var location = GetStackTraceLocation(args.StackTrace);
var stackTrace = GetStackTrace(args.StackTrace);

var consoleMessage = new ConsoleMessage(
ConvertConsoleMessageLevel(args.Method),
text,
handleArgs,
location,
stackTrace);

BidiPage.OnConsole(new ConsoleEventArgs(consoleMessage));
}
else if (args.Type == "javascript")
{
var text = args.Text ?? string.Empty;
var messageLines = new List<string> { text };
Expand Down
2 changes: 2 additions & 0 deletions lib/PuppeteerSharp/Bidi/BidiPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,8 @@ internal static BidiPage From(BidiBrowserContext browserContext, BrowsingContext

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

internal new void OnConsole(ConsoleEventArgs e) => base.OnConsole(e);

internal void OnWorkerCreated(BidiWebWorker worker)
{
_workers[worker.RealmId] = worker;
Expand Down
77 changes: 77 additions & 0 deletions lib/PuppeteerSharp/Bidi/BidiRealm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ internal override async Task EvaluateFunctionAsync(string script, params object[
await EvaluateAsync(true, false, script, args).ConfigureAwait(false);
}

internal IJSHandle CreateHandle(RemoteValue remoteValue)
{
if (
remoteValue.Type is "node" or "window" &&
this is BidiFrameRealm)
{
return BidiElementHandle.From(remoteValue, this);
}

return BidiJSHandle.From(remoteValue, this);
}

protected virtual void ThrowIfDetached()
{
// Base implementation does nothing
Expand Down Expand Up @@ -415,6 +427,13 @@ private async Task<ArgumentValue> FormatArgumentAsync(object arg)

return LocalValue.Array(list.Select(el => el as LocalValue).ToList());
case BidiJSHandle objectHandle:
// If the handle doesn't have a valid handle ID (e.g., from console log args),
// serialize its value directly instead of using it as a remote reference
if (string.IsNullOrEmpty(objectHandle.Id))
{
return SerializeRemoteValue(objectHandle.RemoteValue);
}

return objectHandle.RemoteValue.ToRemoteReference();
case BidiElementHandle elementHandle:
return elementHandle.Value.ToRemoteReference();
Expand All @@ -424,4 +443,62 @@ private async Task<ArgumentValue> FormatArgumentAsync(object arg)

return null;
}

private LocalValue SerializeRemoteValue(RemoteValue remoteValue)
{
return remoteValue.Type switch
{
"undefined" => LocalValue.Undefined,
"null" => LocalValue.Null,
"string" => LocalValue.String((string)remoteValue.Value),
"number" => remoteValue.Value switch
{
long l => LocalValue.Number(l),
double d when double.IsPositiveInfinity(d) => LocalValue.Infinity,
double d when double.IsNegativeInfinity(d) => LocalValue.NegativeInfinity,
double d when double.IsNaN(d) => LocalValue.NaN,
double d => LocalValue.Number(d),
_ => LocalValue.Number(Convert.ToDouble(remoteValue.Value, CultureInfo.InvariantCulture)),
},
"boolean" => LocalValue.Boolean((bool)remoteValue.Value),
"bigint" => LocalValue.BigInt(BigInteger.Parse((string)remoteValue.Value, CultureInfo.InvariantCulture)),
"array" => SerializeRemoteValueArray(remoteValue.Value),
"object" => SerializeRemoteValueObject(remoteValue.Value),
_ => throw new PuppeteerException($"Cannot serialize RemoteValue of type '{remoteValue.Type}' without a handle"),
};
}

private LocalValue SerializeRemoteValueArray(object value)
{
var items = new List<LocalValue>();
if (value is IEnumerable<RemoteValue> remoteValues)
{
foreach (var item in remoteValues)
{
items.Add(SerializeRemoteValue(item));
}
}

return LocalValue.Array(items);
}

private LocalValue SerializeRemoteValueObject(object value)
{
var dict = new Dictionary<string, LocalValue>();
if (value is RemoteValueDictionary remoteDict)
{
foreach (var kvp in remoteDict)
{
var key = kvp.Key switch
{
string s => s,
_ => kvp.Key.ToString(),
};
var val = SerializeRemoteValue(kvp.Value);
dict[key] = val;
}
}

return LocalValue.Object(dict);
}
}
18 changes: 13 additions & 5 deletions lib/PuppeteerSharp/Cdp/CdpPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1127,15 +1127,23 @@ await Task.WhenAll(values.Select(v =>
: RemoteObjectHelper.ValueFromRemoteObject<object>(handle.RemoteObject)?.ToString() ?? "null";
});
var location = new ConsoleMessageLocation();
var stackTraceLocations = new List<ConsoleMessageLocation>();
if (stackTrace?.CallFrames?.Length > 0)
{
var callFrame = stackTrace.CallFrames[0];
location.URL = callFrame.URL;
location.LineNumber = callFrame.LineNumber;
location.ColumnNumber = callFrame.ColumnNumber;
foreach (var callFrame in stackTrace.CallFrames)
{
stackTraceLocations.Add(new ConsoleMessageLocation
{
URL = callFrame.URL,
LineNumber = callFrame.LineNumber,
ColumnNumber = callFrame.ColumnNumber,
});
}

location = stackTraceLocations[0];
}

var consoleMessage = new ConsoleMessage(type, string.Join(" ", tokens), values, location);
var consoleMessage = new ConsoleMessage(type, string.Join(" ", tokens), values, location, stackTraceLocations);
OnConsole(new ConsoleEventArgs(consoleMessage));
}

Expand Down
9 changes: 8 additions & 1 deletion lib/PuppeteerSharp/ConsoleMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ public class ConsoleMessage
/// <param name="text">Text.</param>
/// <param name="args">Arguments.</param>
/// <param name="location">Message location.</param>
public ConsoleMessage(ConsoleType type, string text, IList<IJSHandle> args, ConsoleMessageLocation location = null)
/// <param name="stackTrace">Stack trace.</param>
public ConsoleMessage(ConsoleType type, string text, IList<IJSHandle> args, ConsoleMessageLocation location = null, IList<ConsoleMessageLocation> stackTrace = null)
{
Type = type;
Text = text;
Args = args;
Location = location;
StackTrace = stackTrace ?? [];
}

/// <summary>
Expand All @@ -44,5 +46,10 @@ public ConsoleMessage(ConsoleType type, string text, IList<IJSHandle> args, Cons
/// Gets the location.
/// </summary>
public ConsoleMessageLocation Location { get; }

/// <summary>
/// Gets the stack trace.
/// </summary>
public IList<ConsoleMessageLocation> StackTrace { get; }
}
}
Loading