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

Commit a92e140

Browse files
authored
Merge pull request #644 from grafana/refactor/593-lifecycle
Refactor the frame's lifecycle event handling code
2 parents bae3e73 + c199c80 commit a92e140

File tree

4 files changed

+38
-186
lines changed

4 files changed

+38
-186
lines changed

common/event_emitter.go

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,28 @@ const (
2424

2525
// Frame
2626

27-
EventFrameNavigation string = "navigation"
28-
EventFrameAddLifecycle string = "addlifecycle"
29-
EventFrameRemoveLifecycle string = "removelifecycle"
27+
EventFrameNavigation string = "navigation"
28+
EventFrameAddLifecycle string = "addlifecycle"
3029

3130
// Page
3231

33-
EventPageClose string = "close"
34-
EventPageConsole string = "console"
35-
EventPageCrash string = "crash"
36-
EventPageDialog string = "dialog"
37-
EventPageDOMContentLoaded string = "domcontentloaded"
38-
EventPageDownload string = "download"
39-
EventPageFilechooser string = "filechooser"
40-
EventPageFrameAttached string = "frameattached"
41-
EventPageFrameDetached string = "framedetached"
42-
EventPageFrameNavigated string = "framenavigated"
43-
EventPageLoad string = "load"
44-
EventPageError string = "pageerror"
45-
EventPagePopup string = "popup"
46-
EventPageRequest string = "request"
47-
EventPageRequestFailed string = "requestfailed"
48-
EventPageRequestFinished string = "requestfinished"
49-
EventPageResponse string = "response"
50-
EventPageWebSocket string = "websocket"
51-
EventPageWorker string = "worker"
32+
EventPageClose string = "close"
33+
EventPageConsole string = "console"
34+
EventPageCrash string = "crash"
35+
EventPageDialog string = "dialog"
36+
EventPageDownload string = "download"
37+
EventPageFilechooser string = "filechooser"
38+
EventPageFrameAttached string = "frameattached"
39+
EventPageFrameDetached string = "framedetached"
40+
EventPageFrameNavigated string = "framenavigated"
41+
EventPageError string = "pageerror"
42+
EventPagePopup string = "popup"
43+
EventPageRequest string = "request"
44+
EventPageRequestFailed string = "requestfailed"
45+
EventPageRequestFinished string = "requestfinished"
46+
EventPageResponse string = "response"
47+
EventPageWebSocket string = "websocket"
48+
EventPageWorker string = "worker"
5249

5350
// Session
5451

common/frame.go

Lines changed: 19 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,8 @@ type Frame struct {
5454

5555
// A life cycle event is only considered triggered for a frame if the entire
5656
// frame subtree has also had the life cycle event triggered.
57-
lifecycleEventsMu sync.RWMutex
58-
lifecycleEvents map[LifecycleEvent]bool
59-
subtreeLifecycleEvents map[LifecycleEvent]bool
57+
lifecycleEventsMu sync.RWMutex
58+
lifecycleEvents map[LifecycleEvent]bool
6059

6160
documentHandle *ElementHandle
6261

@@ -65,8 +64,6 @@ type Frame struct {
6564

6665
loadingStartedTime time.Time
6766

68-
networkIdleCh chan struct{}
69-
7067
inflightRequestsMu sync.RWMutex
7168
inflightRequests map[network.RequestID]bool
7269

@@ -94,21 +91,19 @@ func NewFrame(
9491
}
9592

9693
return &Frame{
97-
BaseEventEmitter: NewBaseEventEmitter(ctx),
98-
ctx: ctx,
99-
page: m.page,
100-
manager: m,
101-
parentFrame: parentFrame,
102-
childFrames: make(map[api.Frame]bool),
103-
id: frameID,
104-
vu: k6ext.GetVU(ctx),
105-
lifecycleEvents: make(map[LifecycleEvent]bool),
106-
subtreeLifecycleEvents: make(map[LifecycleEvent]bool),
107-
inflightRequests: make(map[network.RequestID]bool),
108-
executionContexts: make(map[executionWorld]frameExecutionContext),
109-
currentDocument: &DocumentInfo{},
110-
networkIdleCh: make(chan struct{}),
111-
log: log,
94+
BaseEventEmitter: NewBaseEventEmitter(ctx),
95+
ctx: ctx,
96+
page: m.page,
97+
manager: m,
98+
parentFrame: parentFrame,
99+
childFrames: make(map[api.Frame]bool),
100+
id: frameID,
101+
vu: k6ext.GetVU(ctx),
102+
lifecycleEvents: make(map[LifecycleEvent]bool),
103+
inflightRequests: make(map[network.RequestID]bool),
104+
executionContexts: make(map[executionWorld]frameExecutionContext),
105+
currentDocument: &DocumentInfo{},
106+
log: log,
112107
}
113108
}
114109

@@ -168,8 +163,6 @@ func (f *Frame) clearLifecycle() {
168163
f.lifecycleEvents = make(map[LifecycleEvent]bool)
169164
f.lifecycleEventsMu.Unlock()
170165

171-
f.page.frameManager.MainFrame().recalculateLifecycle()
172-
173166
// keep the request related to the document if present
174167
// in f.inflightRequests
175168
f.inflightRequestsMu.Lock()
@@ -188,117 +181,11 @@ func (f *Frame) clearLifecycle() {
188181
f.inflightRequests = inflightRequests
189182
}
190183
f.inflightRequestsMu.Unlock()
191-
192-
f.stopNetworkIdleTimer()
193-
if f.inflightRequestsLen() == 0 {
194-
f.startNetworkIdleTimer()
195-
}
196-
}
197-
198-
func (f *Frame) recalculateLifecycle() {
199-
f.log.Debugf("Frame:recalculateLifecycle", "fid:%s furl:%q", f.ID(), f.URL())
200-
201-
// Start with triggered events.
202-
events := make(map[LifecycleEvent]bool)
203-
f.lifecycleEventsMu.RLock()
204-
{
205-
for k, v := range f.lifecycleEvents {
206-
events[k] = v
207-
}
208-
}
209-
f.lifecycleEventsMu.RUnlock()
210-
211-
// Only consider a life cycle event as fired if it has triggered for all of subtree.
212-
f.childFramesMu.RLock()
213-
{
214-
for child := range f.childFrames {
215-
cf := child.(*Frame)
216-
// a precaution for preventing a deadlock in *Frame.childFramesMu
217-
if cf == f {
218-
continue
219-
}
220-
cf.recalculateLifecycle()
221-
for k := range events {
222-
if !cf.hasSubtreeLifecycleEventFired(k) {
223-
delete(events, k)
224-
}
225-
}
226-
}
227-
}
228-
f.childFramesMu.RUnlock()
229-
230-
// Check if any of the fired events should be considered fired when looking at the entire subtree.
231-
mainFrame := f.manager.MainFrame()
232-
for k := range events {
233-
if f.hasSubtreeLifecycleEventFired(k) {
234-
continue
235-
}
236-
f.emit(EventFrameAddLifecycle, k)
237-
238-
if f != mainFrame {
239-
continue
240-
}
241-
switch k {
242-
case LifecycleEventLoad:
243-
f.page.emit(EventPageLoad, nil)
244-
case LifecycleEventDOMContentLoad:
245-
f.page.emit(EventPageDOMContentLoaded, nil)
246-
}
247-
}
248-
249-
// Emit removal events
250-
f.lifecycleEventsMu.RLock()
251-
{
252-
for k := range f.subtreeLifecycleEvents {
253-
if ok := events[k]; !ok {
254-
f.emit(EventFrameRemoveLifecycle, k)
255-
}
256-
}
257-
}
258-
f.lifecycleEventsMu.RUnlock()
259-
260-
f.lifecycleEventsMu.Lock()
261-
{
262-
f.subtreeLifecycleEvents = make(map[LifecycleEvent]bool)
263-
for k, v := range events {
264-
f.subtreeLifecycleEvents[k] = v
265-
}
266-
}
267-
f.lifecycleEventsMu.Unlock()
268-
}
269-
270-
func (f *Frame) stopNetworkIdleTimer() {
271-
f.log.Debugf("Frame:stopNetworkIdleTimer", "fid:%s furl:%q", f.ID(), f.URL())
272-
273-
select {
274-
case f.networkIdleCh <- struct{}{}:
275-
default:
276-
}
277-
}
278-
279-
func (f *Frame) startNetworkIdleTimer() {
280-
f.log.Debugf("Frame:startNetworkIdleTimer", "fid:%s furl:%q", f.ID(), f.URL())
281-
282-
if f.hasLifecycleEventFired(LifecycleEventNetworkIdle) || f.IsDetached() {
283-
return
284-
}
285-
286-
f.stopNetworkIdleTimer()
287-
288-
go func() {
289-
select {
290-
case <-f.ctx.Done():
291-
case <-f.networkIdleCh:
292-
case <-time.After(LifeCycleNetworkIdleTimeout):
293-
f.manager.frameLifecycleEvent(cdp.FrameID(f.ID()), LifecycleEventNetworkIdle)
294-
}
295-
}()
296184
}
297185

298186
func (f *Frame) detach() {
299187
f.log.Debugf("Frame:detach", "fid:%s furl:%q", f.ID(), f.URL())
300188

301-
f.stopNetworkIdleTimer()
302189
f.setDetached(true)
303190
if f.parentFrame != nil {
304191
f.parentFrame.removeChildFrame(f)
@@ -416,13 +303,6 @@ func (f *Frame) hasLifecycleEventFired(event LifecycleEvent) bool {
416303
return f.lifecycleEvents[event]
417304
}
418305

419-
func (f *Frame) hasSubtreeLifecycleEventFired(event LifecycleEvent) bool {
420-
f.lifecycleEventsMu.RLock()
421-
defer f.lifecycleEventsMu.RUnlock()
422-
423-
return f.subtreeLifecycleEvents[event]
424-
}
425-
426306
func (f *Frame) navigated(name string, url string, loaderID string) {
427307
f.log.Debugf("Frame:navigated", "fid:%s furl:%q lid:%s name:%q url:%q", f.ID(), f.URL(), loaderID, name, url)
428308

@@ -454,12 +334,10 @@ func (f *Frame) onLifecycleEvent(event LifecycleEvent) {
454334
f.log.Debugf("Frame:onLifecycleEvent", "fid:%s furl:%q event:%s", f.ID(), f.URL(), event)
455335

456336
f.lifecycleEventsMu.Lock()
457-
defer f.lifecycleEventsMu.Unlock()
458-
459-
if ok := f.lifecycleEvents[event]; ok {
460-
return
461-
}
462337
f.lifecycleEvents[event] = true
338+
f.lifecycleEventsMu.Unlock()
339+
340+
f.emit(EventFrameAddLifecycle, event)
463341
}
464342

465343
func (f *Frame) onLoadingStarted() {
@@ -478,9 +356,6 @@ func (f *Frame) onLoadingStopped() {
478356
// requests so it may take a long time for us to see
479357
// a networkIdle event or we may never see one if the
480358
// website never stops performing network requests.
481-
// NOTE: This is a different timeout to networkIdleTimer,
482-
// which only works once there are no more network
483-
// requests and we don't see a networkIdle event.
484359
}
485360

486361
func (f *Frame) position() *Position {
@@ -1933,7 +1808,7 @@ func (f *Frame) WaitForNavigation(opts goja.Value) *goja.Promise {
19331808
// A lifecycle event won't be received when navigating within the same
19341809
// document, so don't wait for it. The event might've also already been
19351810
// fired once we're here, so also skip waiting in that case.
1936-
if !sameDocNav && !f.hasSubtreeLifecycleEventFired(parsedOpts.WaitUntil) {
1811+
if !sameDocNav && !f.hasLifecycleEventFired(parsedOpts.WaitUntil) {
19371812
select {
19381813
case <-lifecycleEvtCh:
19391814
case <-timeoutCtx.Done():

common/frame_manager.go

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,6 @@ func (m *FrameManager) removeBarrier(b *Barrier) {
9898
m.barriers = append(m.barriers[:index], m.barriers[index+1:]...)
9999
}
100100

101-
func (m *FrameManager) dispose() {
102-
m.logger.Debugf("FrameManager:dispose", "fmid:%d", m.ID())
103-
104-
m.framesMu.RLock()
105-
defer m.framesMu.RUnlock()
106-
for _, f := range m.frames {
107-
f.stopNetworkIdleTimer()
108-
}
109-
}
110-
111101
func (m *FrameManager) frameAbortedNavigation(frameID cdp.FrameID, errorText, documentID string) {
112102
m.logger.Debugf("FrameManager:frameAbortedNavigation",
113103
"fmid:%d fid:%v err:%s docid:%s",
@@ -201,7 +191,6 @@ func (m *FrameManager) frameLifecycleEvent(frameID cdp.FrameID, event LifecycleE
201191
frame := m.getFrameByID(frameID)
202192
if frame != nil {
203193
frame.onLifecycleEvent(event)
204-
m.MainFrame().recalculateLifecycle() // Recalculate life cycle state from the top
205194
}
206195
}
207196

@@ -452,8 +441,6 @@ func (m *FrameManager) requestFailed(req *Request, canceled bool) {
452441

453442
ifr := frame.cloneInflightRequests()
454443
switch rc := len(ifr); {
455-
case rc == 0:
456-
frame.startNetworkIdleTimer()
457444
case rc <= 10:
458445
for reqID := range ifr {
459446
req := frame.requestByID(reqID)
@@ -503,9 +490,6 @@ func (m *FrameManager) requestFinished(req *Request) {
503490
return
504491
}
505492
frame.deleteRequest(req.getID())
506-
if frame.inflightRequestsLen() == 0 {
507-
frame.startNetworkIdleTimer()
508-
}
509493
/*
510494
else if frame.inflightRequestsLen() <= 10 {
511495
for reqID, _ := range frame.inflightRequests {
@@ -537,9 +521,6 @@ func (m *FrameManager) requestStarted(req *Request) {
537521
}
538522

539523
frame.addRequest(req.getID())
540-
if frame.inflightRequestsLen() == 1 {
541-
frame.stopNetworkIdleTimer()
542-
}
543524
if req.documentID != "" {
544525
frame.pendingDocumentMu.Lock()
545526
frame.pendingDocument = &DocumentInfo{documentID: req.documentID, request: req}

common/page.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ func (p *Page) didClose() {
163163
func (p *Page) didCrash() {
164164
p.logger.Debugf("Page:didCrash", "sid:%v", p.sessionID())
165165

166-
p.frameManager.dispose()
167166
p.emit(EventPageCrash, p)
168167
}
169168

0 commit comments

Comments
 (0)