Skip to content

Commit a401acc

Browse files
committed
feat: Browser-scoped logger
Add support for each browser to have it's own logger, allowing test code to have higher control of logging, but using individual settings in a test-by-test case.
1 parent 73cbc02 commit a401acc

34 files changed

+403
-300
lines changed

browser.go

+15-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package browser
22

33
import (
44
"fmt"
5+
"log/slog"
56
"net/http"
67

78
"github.com/gost-dom/browser/html"
@@ -11,12 +12,17 @@ import (
1112
"github.com/gost-dom/browser/scripting/v8host"
1213
)
1314

15+
type BrowserOption func(*Browser)
16+
17+
func Logger(l *slog.Logger) BrowserOption { return func(b *Browser) { b.Logger = l } }
18+
1419
// Pretty stupid right now, but should _probably_ allow handling multiple
1520
// windows/tabs. This used to be the case for _some_ identity providers, but I'm
1621
// not sure if that even work anymore because of browser security.
1722
type Browser struct {
1823
Client http.Client
1924
ScriptHost ScriptHost
25+
Logger log.Logger
2026
windows []Window
2127
}
2228

@@ -58,21 +64,25 @@ func NewFromHandler(handler http.Handler) *Browser {
5864
}
5965

6066
// New initialises a new [Browser] with the default script engine.
61-
func New() *Browser {
62-
return &Browser{
67+
func New(options ...func(*Browser)) *Browser {
68+
result := &Browser{
6369
ScriptHost: v8host.New(),
6470
Client: NewHttpClient(),
6571
}
72+
for _, opt := range options {
73+
opt(result)
74+
}
75+
return result
6676
}
6777

68-
// NewBrowser should not be called. Call New instead.
78+
// Deprecated: NewBrowser should not be called. Call New instead.
6979
//
7080
// This method will selfdestruct in 10 commits
7181
func NewBrowser() *Browser {
7282
return New()
7383
}
7484

75-
// NewBrowserFromHandler should not be called, call, NewFromHandler instead.
85+
// Deprecated: NewBrowserFromHandler should not be called, call, NewFromHandler instead.
7686
//
7787
// This method will selfdestruct in 10 commits
7888
func NewBrowserFromHandler(handler http.Handler) *Browser {
@@ -88,7 +98,7 @@ func (b *Browser) createOptions(location string) WindowOptions {
8898
}
8999

90100
func (b *Browser) Close() {
91-
log.Debug("Browser: Close()")
101+
log.Debug(b.Logger, "Browser: Close()")
92102
for _, win := range b.windows {
93103
win.Close()
94104
}

docs/Features.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Specific HTML elements have specific behaviour.
8787

8888
The list of supported user interaction is yet quite limited
8989

90-
- `HTMLElement.click()` - implemented on `dom.Element`, due to some weirdness in
90+
- `HTMLElemet.click()` - implemented on `dom.Element`, due to some weirdness in
9191
the specs.
9292

9393
### URL API

dom/document.go

+8
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package dom
22

33
import (
44
"io"
5+
"log/slog"
56

67
"github.com/gost-dom/browser/dom/event"
8+
"github.com/gost-dom/browser/internal/log"
79
"golang.org/x/net/html"
810
)
911

@@ -44,6 +46,7 @@ type elementConstructor func(doc *document) Element
4446

4547
type document struct {
4648
rootNode
49+
logger *slog.Logger
4750
activeElement Element
4851
ownerWindow DocumentParentWindow
4952
}
@@ -58,9 +61,14 @@ func NewDocument(window DocumentParentWindow) Document {
5861
// What about disconnected documents, e.g. `new Document()` in the browser?
5962
result.SetParentTarget(window)
6063
result.SetSelf(result)
64+
if logger, isLogSource := window.(log.LogSource); isLogSource {
65+
result.logger = logger.Logger()
66+
}
6167
return result
6268
}
6369

70+
func (d document) Logger() *slog.Logger { return d.logger }
71+
6472
func (d document) ActiveElement() Element {
6573
if d.activeElement == nil {
6674
return d.Body()

dom/event/event_target.go

+19-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package event
22

33
import (
4+
"log/slog"
45
"slices"
56

67
"github.com/gost-dom/browser/internal/log"
@@ -56,9 +57,7 @@ func NewEventTarget() EventTarget {
5657
return &res
5758
}
5859

59-
func (t *eventTarget) SetParentTarget(parent EventTarget) {
60-
t.parentTarget = parent
61-
}
60+
func (t *eventTarget) SetParentTarget(parent EventTarget) { t.parentTarget = parent }
6261

6362
func SetEventTargetSelf(t EventTarget) {
6463
t.setSelf(t)
@@ -91,7 +90,7 @@ func (e *eventTarget) AddEventListener(
9190
options ...func(*EventListener),
9291
) {
9392
listener := e.createListener(handler, options)
94-
log.Debug("AddEventListener", "EventType", eventType)
93+
log.Debug(e.logger(), "AddEventListener", "EventType", eventType)
9594
// TODO: Handle options
9695
// - once
9796
// - passive. Defaults to false,
@@ -134,7 +133,7 @@ func (e *eventTarget) SetCatchAllHandler(handler EventHandler) {
134133
}
135134

136135
func (e *eventTarget) DispatchEvent(event *Event) bool {
137-
log.Debug("Dispatch event", "EventType", event.Type)
136+
log.Debug(e.logger(), "Dispatch event", "EventType", event.Type)
138137
event.target = e.self
139138
event.stopped = false
140139
event.cancelled = false
@@ -162,15 +161,20 @@ func (e *eventTarget) dispatchEvent(event *Event, capture bool) {
162161
defer func() { event.currentTarget = nil }()
163162
if e.catchAllHandler != nil && !capture {
164163
if err := e.catchAllHandler.HandleEvent(event); err != nil {
165-
log.Debug("Error occurred", "error", err.Error())
164+
log.Debug(e.logger(), "Error occurred", "error", err.Error())
166165
e.dispatchError(err)
167166
}
168167
}
169168

170169
listeners := e.lmap[event.Type]
171170
for i := 0; i < len(listeners); i++ {
172171
l := listeners[i]
173-
log.Debug("eventTarget.dispatchEvent: Calling event handler", "type", event.Type)
172+
log.Debug(
173+
e.logger(),
174+
"eventTarget.dispatchEvent: Calling event handler",
175+
"type",
176+
event.Type,
177+
)
174178
if l.Capture == capture {
175179
if err := l.Handler.HandleEvent(event); err != nil {
176180
e.handleError(err)
@@ -185,7 +189,7 @@ func (e *eventTarget) dispatchEvent(event *Event, capture bool) {
185189
}
186190

187191
func (e *eventTarget) handleError(err error) {
188-
log.Error(
192+
log.Error(e.logger(),
189193
"eventTarget.dispatchEvent: Error occurred in event handler",
190194
"error",
191195
err.Error(),
@@ -215,3 +219,10 @@ func (e *eventTarget) dispatchError(err error) {
215219
e.parentTarget.dispatchError(err)
216220
}
217221
}
222+
223+
func (e *eventTarget) logger() *slog.Logger {
224+
if source, ok := e.self.(log.LogSource); ok {
225+
return source.Logger()
226+
}
227+
return nil
228+
}

dom/node.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dom
33
import (
44
"errors"
55
"fmt"
6+
"log/slog"
67
"slices"
78
"strconv"
89
"strings"
@@ -265,7 +266,7 @@ func (c observerCloser) Close() {
265266

266267
func (n *node) GetRootNode(options ...GetRootNodeOptions) Node {
267268
if len(options) > 1 {
268-
log.Warn("Node.GetRootNode: composed not yet implemented")
269+
log.Warn(n.logger(), "Node.GetRootNode: composed not yet implemented")
269270
}
270271
if n.parent == nil {
271272
return n.self
@@ -559,3 +560,10 @@ func (n *node) removedNodeEvent(nodes ...Node) ChangeEvent {
559560
RemovedNodes: &nodeList{nodes: nodes},
560561
}
561562
}
563+
564+
func (n *node) logger() *slog.Logger {
565+
if docLogger, ok := n.document.(log.LogSource); ok {
566+
return docLogger.Logger()
567+
}
568+
return nil
569+
}

html/history.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package html
22

33
import (
4+
"log/slog"
5+
46
"github.com/gost-dom/browser/dom/event"
57
"github.com/gost-dom/browser/internal/log"
68
)
@@ -123,7 +125,7 @@ func (h History) currentIdx() int {
123125
//
124126
// [replaceState on the History API]: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState
125127
func (h *History) PushState(state HistoryState, href string) error {
126-
log.Info("History.PushState", "href", href)
128+
log.Info(h.logger(), "History.PushState", "href", href)
127129
newHref := h.window.setBaseLocation(href)
128130
h.pushHistoryEntry(historyEntry{state: state, href: newHref, remote: false})
129131
return nil
@@ -147,6 +149,8 @@ func (h History) State() HistoryState {
147149
return h.entries[h.currentIdx()].state
148150
}
149151

152+
func (h *History) logger() *slog.Logger { return h.window.Logger() }
153+
150154
type popStateEvent struct {
151155
event.Event
152156
state HistoryState

html/html_element.go

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package html
22

33
import (
4+
"log/slog"
45
"strconv"
56
"strings"
67

@@ -106,3 +107,10 @@ func (e *htmlElement) SetAutofocus(val bool) {
106107
e.RemoveAttribute("autofocus")
107108
}
108109
}
110+
111+
func (e *htmlElement) logger() *slog.Logger {
112+
if win := e.window(); win != nil {
113+
return win.Logger()
114+
}
115+
return nil
116+
}

html/html_script_element.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func (e *htmlScriptElement) Connected() {
3030
}
3131
if resp.StatusCode != 200 {
3232
body, _ := io.ReadAll(resp.Body)
33-
log.Error("Error from server", "body", string(body), "src", src)
33+
log.Error(e.logger(), "Error from server", "body", string(body), "src", src)
3434
panic("Bad response")
3535
}
3636

html/window.go

+23-16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package html
33
import (
44
"errors"
55
"io"
6+
"log/slog"
67
"net/http"
78
netURL "net/url"
89
"strings"
@@ -27,6 +28,7 @@ type Clock interface {
2728

2829
// Describes a current browsering context
2930
type BrowsingContext interface {
31+
log.LogSource
3032
HTTPClient() http.Client
3133
LocationHREF() string
3234
}
@@ -76,6 +78,7 @@ type window struct {
7678
httpClient http.Client
7779
baseLocation string
7880
domParser domParser
81+
logger *slog.Logger
7982
}
8083

8184
func newWindow(windowOptions ...WindowOption) *window {
@@ -88,6 +91,7 @@ func newWindow(windowOptions ...WindowOption) *window {
8891
httpClient: options.HttpClient,
8992
baseLocation: options.BaseLocation,
9093
scriptEngineFactory: options.ScriptHost,
94+
logger: options.Logger,
9195
history: new(History),
9296
}
9397
if win.baseLocation == "" {
@@ -193,20 +197,6 @@ func (w *window) parseReader(reader io.Reader) error {
193197

194198
func (w *window) HTTPClient() http.Client { return w.httpClient }
195199

196-
type WindowOptions struct {
197-
ScriptHost
198-
HttpClient http.Client
199-
BaseLocation string
200-
}
201-
202-
type WindowOption interface {
203-
Apply(options *WindowOptions)
204-
}
205-
206-
type WindowOptionFunc func(*WindowOptions)
207-
208-
func (f WindowOptionFunc) Apply(options *WindowOptions) { f(options) }
209-
210200
func WindowOptionLocation(location string) WindowOptionFunc {
211201
return func(options *WindowOptions) {
212202
options.BaseLocation = location
@@ -234,7 +224,7 @@ func (w *window) handleResponse(resp *http.Response) error {
234224
}
235225

236226
func (w *window) Navigate(href string) error {
237-
log.Info("Window.navigate:", "href", href)
227+
log.Info(w.Logger(), "Window.navigate:", "href", href)
238228
w.History().pushLoad(href)
239229
w.initScriptEngine()
240230
w.baseLocation = href
@@ -253,7 +243,7 @@ func (w *window) Navigate(href string) error {
253243
// reload is used internally to load a page into the browser, but without
254244
// affecting the history
255245
func (w *window) reload(href string) error {
256-
log.Debug("Window.reload:", "href", href)
246+
log.Debug(w.Logger(), "Window.reload:", "href", href)
257247
w.initScriptEngine()
258248
w.baseLocation = href
259249
if href == "about:blank" {
@@ -327,3 +317,20 @@ func (w *window) resolveHref(href string) *url.URL {
327317
}
328318
return r
329319
}
320+
321+
func (w *window) Logger() log.Logger { return w.logger }
322+
323+
type WindowOptions struct {
324+
ScriptHost
325+
HttpClient http.Client
326+
BaseLocation string
327+
Logger *slog.Logger
328+
}
329+
330+
type WindowOption interface {
331+
Apply(options *WindowOptions)
332+
}
333+
334+
type WindowOptionFunc func(*WindowOptions)
335+
336+
func (f WindowOptionFunc) Apply(options *WindowOptions) { f(options) }

internal/code-gen/script-wrappers/v8_generators.go

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ func CreateV8FunctionTemplateCallbackBody(
134134
) JenGenerator {
135135
naming := V8NamingStrategy{data}
136136
debug := g.NewValuePackage("Debug", packagenames.Log).Call(
137+
g.NewValue(naming.Receiver()).Field("logger").Call(g.Id("info")),
137138
g.Lit(fmt.Sprintf("V8 Function call: %s.%s", data.Name(), op.Name)))
138139
if op.NotImplemented {
139140
errMsg := fmt.Sprintf(

0 commit comments

Comments
 (0)