Documentation
¶
Overview ¶
Package js runs a page's JavaScript against a minimal but real DOM binding on a pure-Go goja ECMAScript runtime. It is deliberately not a full WHATWG implementation: the goal is progressive-enhancement and light hydration — class toggling, attribute/style mutation, small tree edits — so that the subsequent CSS cascade and layout see what a real browser would.
All mutations write through to the shared dom.Node tree. Execution is bounded by a wall-clock budget and a script error never propagates: it is caught, optionally logged, and the render continues.
Index ¶
Constants ¶
const DefaultTimeout = 5 * time.Second
DefaultTimeout is the script budget when Options.Timeout is zero.
Variables ¶
This section is empty.
Functions ¶
func MarkJSEnabled ¶
MarkJSEnabled sets the client-js signal on the document root (swapping the client-nojs no-JS fallback for client-js on <html>) without running any script. The engine calls it before the INITIAL cascade so the first layout — the geometry scripts read back — already reflects a JS-enabled browser.
Types ¶
type Metrics ¶
type Metrics interface {
// Rect returns the used border-box rectangle (document coords, CSS px) of n.
// ok is false when n was not laid out (display:none or detached).
Rect(n *dom.Node) (x, y, w, h float64, ok bool)
// Computed returns the resolved used value of a CSS property (lower-case,
// hyphenated — e.g. "width", "display", "margin-top", "font-size"). ok is
// false for a property the resolver does not model, matching a browser
// returning "" for an unsupported computed property.
Computed(n *dom.Node, prop string) (string, bool)
}
Metrics supplies real used geometry and resolved (used) style values from a completed cascade+layout pass. The DOM binding reads it so that getBoundingClientRect / offset* / client* / scroll* and window.getComputedStyle answer with real numbers instead of zeros — the signal responsive scripts and MediaWiki's mw.loader consult to decide what to collapse or reveal. A nil Metrics (the legacy js.Run path) makes those APIs report zeros / inline styles, exactly as before.
type Options ¶
type Options struct {
// PageURL is the document's final URL, used to resolve script src and to
// populate window.location.
PageURL string
// UserAgent is exposed as navigator.userAgent and sent when fetching scripts.
UserAgent string
// Client fetches external <script src>. When nil, external scripts are skipped
// (inline scripts still run).
Client *http.Client
// Ctx bounds network fetches; its cancellation also stops script execution.
Ctx context.Context
// Timeout is the total wall-clock budget for all scripts and drained timers.
// Zero selects DefaultTimeout.
Timeout time.Duration
// ViewportWidth / ViewportHeight back window.innerWidth / innerHeight.
ViewportWidth int
ViewportHeight int
// Log, if non-nil, receives console.* output as "level: message" lines.
Log func(string)
}
Options configures a Run.
type Result ¶
type Result struct {
ScriptsRun int // scripts that executed (inline + fetched)
ScriptsFailed int // scripts that threw or failed to compile
TimersRun int // queued timer/animation callbacks drained
Err error // first fatal setup error (nil on normal completion)
}
Result reports what a Run did (used by tests and by the engine's logging).
func Run ¶
Run builds the DOM binding on root (a dom.Document node), sets the JS-enabled signal, executes the page's scripts in document order, drains queued timers, and dispatches DOMContentLoaded/load. It never returns a script error as fatal; Result.Err is only set for setup failures. It is the one-shot, no-layout-feedback entry point (used by the js package's own tests); the engine drives a Session directly so scripts can read real geometry.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session is a live page-script execution bound to one DOM tree and one goja runtime. Unlike the one-shot Run, it stays resident across layout passes: the engine lays the page out, feeds the geometry in via SetMetrics, runs scripts (which may read that geometry and mutate the DOM / inject <script>/<style>), re-lays-out, and runs any newly-injected scripts — iterating to a bounded fixpoint before the final paint.
func Begin ¶
Begin builds the DOM/BOM binding on a fresh runtime, sets the client-js signal, and starts the wall-clock/interrupt watchdog. The caller MUST call Close to release the watchdog goroutine. A binding-setup panic is contained; Begin always returns a usable (possibly inert) Session.
func (*Session) RunInitial ¶
func (s *Session) RunInitial()
RunInitial executes every page <script> in document order, then dispatches DOMContentLoaded/load, draining queued timers/promises/XHR callbacks to quiescence within the budget. Contained against panics.
func (*Session) RunPending ¶
RunPending executes any <script> elements inserted into the DOM since the last run (the core of a ResourceLoader-style dynamic loader) in document order, then drains the async loop. It reports whether at least one new script ran.
func (*Session) SetMetrics ¶
SetMetrics installs (or replaces) the geometry/used-value source the DOM binding reads back. Call it after each layout pass, before running scripts.