//go:build browser package gallery import ( "bytes" "context" "fmt" "image/png" "net/http" "net/http/httptest" "regexp" "strconv" "strings" "sync" "testing" "time" "github.com/chromedp/cdproto/emulation" cdppage "github.com/chromedp/cdproto/page" "github.com/chromedp/chromedp" "amadan.net/rastrillo/fiesta" "amadan.net/rastrillo/fiesta/internal/gallery/galleryrig" "amadan.net/rastrillo/fiesta/internal/harness" ) // Frames hidden until styled, and shown in the page's scheme: the // arm, seek and watch half of view.js, driven with a frame document // held at each of the moments a reader could see the wrong thing. // Rastrillo's drives; the held document is served without frame.js, // because frame.js copies the parent's scheme before the first paint // and these legs are about the window a document without one opens, // which is every document a navigation inside a frame loads from // elsewhere. // gate is one held response: a handler blocks on it until the leg has // looked at the state the hold keeps open. Opened at most once. A nil // gate holds nothing. type gate struct { c chan struct{} once sync.Once } func newGate() *gate { return &gate{c: make(chan struct{})} } func (g *gate) open() { if g != nil { g.once.Do(func() { close(g.c) }) } } func (g *gate) wait() { if g != nil { <-g.c } } // holds are the places heldFrames can hold the first frame on a page: // start, its document before a byte of it is sent; sheet, the // stylesheets it links; parse, the rest of its document after the // body's first paragraph; load, an image in that rest; next and // nextLoad, the same two for the document a navigation inside the // frame fetches with ?next. type holds struct{ start, sheet, parse, load, next, nextLoad *gate } func (h *holds) release() { for _, g := range []*gate{h.start, h.sheet, h.parse, h.load, h.next, h.nextLoad} { g.open() } } const held, heldNext = mountPath + "/held.png", mountPath + "/held-next.png" var ( firstFrame = regexp.MustCompile(`