Skip to content
This repository was archived by the owner on Jan 30, 2025. It is now read-only.

Commit 3aca99a

Browse files
author
Ivan Mirić
authored
Merge pull request #130 from grafana/fix/120-panic-page-goto
Implement RemoteObject conversion to native types
2 parents 99d1ec9 + 152b56d commit 3aca99a

15 files changed

+672
-296
lines changed

common/execution_context.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,25 @@ var /* const */ sourceURLRegex = regexp.MustCompile(`^(?s)[\040\t]*//[@#] source
4040
// ExecutionContext represents a JS execution context
4141
type ExecutionContext struct {
4242
ctx context.Context
43+
logger *Logger
4344
session *Session
4445
frame *Frame
4546
id runtime.ExecutionContextID
4647
injectedScript api.JSHandle
4748
}
4849

4950
// NewExecutionContext creates a new JS execution context
50-
func NewExecutionContext(ctx context.Context, session *Session, frame *Frame, id runtime.ExecutionContextID) *ExecutionContext {
51+
func NewExecutionContext(
52+
ctx context.Context, session *Session, frame *Frame,
53+
id runtime.ExecutionContextID, logger *Logger,
54+
) *ExecutionContext {
5155
return &ExecutionContext{
5256
ctx: ctx,
5357
session: session,
5458
frame: frame,
5559
id: id,
5660
injectedScript: nil,
61+
logger: logger,
5762
}
5863
}
5964

@@ -69,7 +74,7 @@ func (e *ExecutionContext) adoptBackendNodeId(backendNodeID cdp.BackendNodeID) (
6974
return nil, fmt.Errorf("unable to get DOM node: %w", err)
7075
}
7176

72-
return NewJSHandle(e.ctx, e.session, e, e.frame, remoteObj).AsElement().(*ElementHandle), nil
77+
return NewJSHandle(e.ctx, e.session, e, e.frame, remoteObj, e.logger).AsElement().(*ElementHandle), nil
7378
}
7479

7580
// Adopts the specified element handle into this execution context from another execution context
@@ -129,7 +134,7 @@ func (e *ExecutionContext) evaluate(apiCtx context.Context, forceCallable bool,
129134
}
130135
} else if remoteObject.ObjectID != "" {
131136
// Note: we don't use the passed in apiCtx here as it could be tied to a timeout
132-
res = NewJSHandle(e.ctx, e.session, e, e.frame, remoteObject)
137+
res = NewJSHandle(e.ctx, e.session, e, e.frame, remoteObject, e.logger)
133138
}
134139
}
135140
} else {
@@ -165,7 +170,7 @@ func (e *ExecutionContext) evaluate(apiCtx context.Context, forceCallable bool,
165170
}
166171
} else if remoteObject.ObjectID != "" {
167172
// Note: we don't use the passed in apiCtx here as it could be tied to a timeout
168-
res = NewJSHandle(e.ctx, e.session, e, e.frame, remoteObject)
173+
res = NewJSHandle(e.ctx, e.session, e, e.frame, remoteObject, e.logger)
169174
}
170175
}
171176
}

common/frame_session.go

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"github.com/chromedp/cdproto/security"
4242
"github.com/chromedp/cdproto/target"
4343
"github.com/grafana/xk6-browser/api"
44+
"github.com/sirupsen/logrus"
4445
k6lib "go.k6.io/k6/lib"
4546
k6stats "go.k6.io/k6/stats"
4647
)
@@ -77,9 +78,14 @@ type FrameSession struct {
7778
childSessions map[cdp.FrameID]*FrameSession
7879

7980
logger *Logger
81+
// logger that will properly serialize RemoteObject instances
82+
serializer *logrus.Logger
8083
}
8184

82-
func NewFrameSession(ctx context.Context, session *Session, page *Page, parent *FrameSession, targetID target.ID, logger *Logger) (*FrameSession, error) {
85+
func NewFrameSession(
86+
ctx context.Context, session *Session, page *Page, parent *FrameSession,
87+
targetID target.ID, logger *Logger,
88+
) (*FrameSession, error) {
8389
logger.Debugf("NewFrameSession", "sid:%v tid:%v", session.id, targetID)
8490
fs := FrameSession{
8591
ctx: ctx, // TODO: create cancelable context that can be used to cancel and close all child sessions
@@ -96,6 +102,11 @@ func NewFrameSession(ctx context.Context, session *Session, page *Page, parent *
96102
eventCh: make(chan Event),
97103
childSessions: make(map[cdp.FrameID]*FrameSession),
98104
logger: logger,
105+
serializer: &logrus.Logger{
106+
Out: logger.log.Out,
107+
Level: logger.log.Level,
108+
Formatter: &consoleLogFormatter{logger.log.Formatter},
109+
},
99110
}
100111
var err error
101112
if fs.parent != nil {
@@ -455,33 +466,34 @@ func (fs *FrameSession) navigateFrame(frame *Frame, url, referrer string) (strin
455466

456467
func (fs *FrameSession) onConsoleAPICalled(event *runtime.EventConsoleAPICalled) {
457468
// TODO: switch to using browser logger instead of directly outputting to k6 logging system
458-
state := k6lib.GetState(fs.ctx)
459-
l := state.Logger.
469+
l := fs.serializer.
460470
WithTime(event.Timestamp.Time()).
461471
WithField("source", "browser-console-api")
462-
if state.Group.Path != "" {
463-
l = l.WithField("group", state.Group.Path)
472+
473+
if s := k6lib.GetState(fs.ctx); s.Group.Path != "" {
474+
l = l.WithField("group", s.Group.Path)
464475
}
465-
var convertedArgs []interface{}
466-
for _, arg := range event.Args {
467-
i, err := interfaceFromRemoteObject(arg)
476+
477+
var parsedObjects []interface{}
478+
for _, robj := range event.Args {
479+
i, err := parseRemoteObject(robj)
468480
if err != nil {
469-
// TODO(fix): this should not throw!
470-
k6Throw(fs.ctx, "unable to parse remote object value: %w", err)
481+
handleParseRemoteObjectErr(fs.ctx, err, l)
471482
}
472-
convertedArgs = append(convertedArgs, i)
483+
parsedObjects = append(parsedObjects, i)
473484
}
485+
486+
l = l.WithField("objects", parsedObjects)
487+
474488
switch event.Type {
475-
case "log":
476-
l.Info(convertedArgs...)
477-
case "info":
478-
l.Info(convertedArgs...)
489+
case "log", "info":
490+
l.Info()
479491
case "warning":
480-
l.Warn(convertedArgs...)
492+
l.Warn()
481493
case "error":
482-
l.Error(convertedArgs...)
494+
l.Error()
483495
default:
484-
l.Debug(convertedArgs...)
496+
l.Debug()
485497
}
486498
}
487499

@@ -517,7 +529,7 @@ func (fs *FrameSession) onExecutionContextCreated(event *runtime.EventExecutionC
517529
if i.Type == "isolated" {
518530
fs.isolatedWorlds[event.Context.Name] = true
519531
}
520-
context := NewExecutionContext(fs.ctx, fs.session, frame, event.Context.ID)
532+
context := NewExecutionContext(fs.ctx, fs.session, frame, event.Context.ID, fs.logger)
521533
if world != "" {
522534
frame.setContext(world, context)
523535
}

common/helpers.go

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
"math"
2929
"os"
3030
"reflect"
31-
"strconv"
3231
"strings"
3332
"time"
3433

@@ -168,42 +167,6 @@ func errorFromDOMError(domErr string) error {
168167
return fmt.Errorf(domErr)
169168
}
170169

171-
func interfaceFromRemoteObject(remoteObject *cdpruntime.RemoteObject) (interface{}, error) {
172-
if remoteObject.ObjectID != "" {
173-
return nil, ErrUnexpectedRemoteObjectWithID
174-
}
175-
if remoteObject.UnserializableValue != "" {
176-
if remoteObject.Type == "bigint" {
177-
n, err := strconv.ParseInt(strings.Replace(remoteObject.UnserializableValue.String(), "n", "", -1), 10, 64)
178-
if err != nil {
179-
return nil, BigIntParseError{err}
180-
}
181-
return n, nil
182-
}
183-
switch remoteObject.UnserializableValue.String() {
184-
case "-0": // To handle +0 divided by negative number
185-
return math.Float64frombits(0 | (1 << 63)), nil
186-
case "NaN":
187-
return math.NaN(), nil
188-
case "Infinity":
189-
return math.Inf(0), nil
190-
case "-Infinity":
191-
return math.Inf(-1), nil
192-
default:
193-
return nil, UnserializableValueError{remoteObject.UnserializableValue}
194-
}
195-
}
196-
if remoteObject.Type == "undefined" {
197-
return "undefined", nil
198-
}
199-
var i interface{}
200-
err := json.Unmarshal(remoteObject.Value, &i)
201-
if err != nil {
202-
return nil, err
203-
}
204-
return i, nil
205-
}
206-
207170
func stringSliceContains(s []string, e string) bool {
208171
for _, a := range s {
209172
if a == e {
@@ -213,15 +176,6 @@ func stringSliceContains(s []string, e string) bool {
213176
return false
214177
}
215178

216-
func valueFromRemoteObject(ctx context.Context, remoteObject *cdpruntime.RemoteObject) (goja.Value, error) {
217-
rt := k6common.GetRuntime(ctx)
218-
i, err := interfaceFromRemoteObject(remoteObject)
219-
if i == "undefined" {
220-
return goja.Undefined(), err
221-
}
222-
return rt.ToValue(i), err
223-
}
224-
225179
func createWaitForEventHandler(
226180
ctx context.Context,
227181
emitter EventEmitter, events []string,

0 commit comments

Comments
 (0)