mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(oopifs): ignore events from stale frames (#8926)
This is a speculative fix for the following scenario: - Main frame A creates a child frame B. - B navigates cross-origin and forces an oopif. - Target.attachedToTarget for B arrives. - B loads and creates execution contexts. - Process with A creates an execution context in the (still local) frame B (e.g. with document.write?) - Process with A sends executionContextCreated and overwrites the execution context that came from B. - Process with A finally sends frameDetached for B, and we ignore it since we already have the B target. This sequence results in a stale execution context from process A that is actually not present in B. Overall, events coming from process A for the frame that has already moved to an oopif B should be ignored. Seems totally safe! This is also pure specultation from analyzing protocol logs, no easy repro found.
This commit is contained in:
parent
04fa5e6b2d
commit
2ec82b9a5c
@ -136,7 +136,7 @@ export class CRPage implements PageDelegate {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _sessionForFrame(frame: frames.Frame): FrameSession {
|
_sessionForFrame(frame: frames.Frame): FrameSession {
|
||||||
// Frame id equals target id.
|
// Frame id equals target id.
|
||||||
while (!this._sessions.has(frame._id)) {
|
while (!this._sessions.has(frame._id)) {
|
||||||
const parent = frame.parentFrame();
|
const parent = frame.parentFrame();
|
||||||
@ -553,6 +553,8 @@ class FrameSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onLifecycleEvent(event: Protocol.Page.lifecycleEventPayload) {
|
_onLifecycleEvent(event: Protocol.Page.lifecycleEventPayload) {
|
||||||
|
if (this._eventBelongsToStaleFrame(event.frameId))
|
||||||
|
return;
|
||||||
if (event.name === 'load')
|
if (event.name === 'load')
|
||||||
this._page._frameManager.frameLifecycleEvent(event.frameId, 'load');
|
this._page._frameManager.frameLifecycleEvent(event.frameId, 'load');
|
||||||
else if (event.name === 'DOMContentLoaded')
|
else if (event.name === 'DOMContentLoaded')
|
||||||
@ -560,6 +562,8 @@ class FrameSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onFrameStoppedLoading(frameId: string) {
|
_onFrameStoppedLoading(frameId: string) {
|
||||||
|
if (this._eventBelongsToStaleFrame(frameId))
|
||||||
|
return;
|
||||||
this._page._frameManager.frameStoppedLoading(frameId);
|
this._page._frameManager.frameStoppedLoading(frameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,6 +577,19 @@ class FrameSession {
|
|||||||
this._handleFrameTree(child);
|
this._handleFrameTree(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _eventBelongsToStaleFrame(frameId: string) {
|
||||||
|
const frame = this._page._frameManager.frame(frameId);
|
||||||
|
// Subtree may be already gone because some ancestor navigation destroyed the oopif.
|
||||||
|
if (!frame)
|
||||||
|
return true;
|
||||||
|
// When frame goes remote, parent process may still send some events
|
||||||
|
// related to the local frame before it sends frameDetached.
|
||||||
|
// In this case, we already have a new session for this frame, so events
|
||||||
|
// in the old session should be ignored.
|
||||||
|
const session = this._crPage._sessionForFrame(frame);
|
||||||
|
return session && session !== this && !session._swappedIn;
|
||||||
|
}
|
||||||
|
|
||||||
_onFrameAttached(frameId: string, parentFrameId: string | null) {
|
_onFrameAttached(frameId: string, parentFrameId: string | null) {
|
||||||
const frameSession = this._crPage._sessions.get(frameId);
|
const frameSession = this._crPage._sessions.get(frameId);
|
||||||
if (frameSession && frameId !== this._targetId) {
|
if (frameSession && frameId !== this._targetId) {
|
||||||
@ -595,19 +612,23 @@ class FrameSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onFrameNavigated(framePayload: Protocol.Page.Frame, initial: boolean) {
|
_onFrameNavigated(framePayload: Protocol.Page.Frame, initial: boolean) {
|
||||||
if (!this._page._frameManager.frame(framePayload.id))
|
if (this._eventBelongsToStaleFrame(framePayload.id))
|
||||||
return; // Subtree may be already gone because some ancestor navigation destroyed the oopif.
|
return;
|
||||||
this._page._frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url + (framePayload.urlFragment || ''), framePayload.name || '', framePayload.loaderId, initial);
|
this._page._frameManager.frameCommittedNewDocumentNavigation(framePayload.id, framePayload.url + (framePayload.urlFragment || ''), framePayload.name || '', framePayload.loaderId, initial);
|
||||||
if (!initial)
|
if (!initial)
|
||||||
this._firstNonInitialNavigationCommittedFulfill();
|
this._firstNonInitialNavigationCommittedFulfill();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFrameRequestedNavigation(payload: Protocol.Page.frameRequestedNavigationPayload) {
|
_onFrameRequestedNavigation(payload: Protocol.Page.frameRequestedNavigationPayload) {
|
||||||
|
if (this._eventBelongsToStaleFrame(payload.frameId))
|
||||||
|
return;
|
||||||
if (payload.disposition === 'currentTab')
|
if (payload.disposition === 'currentTab')
|
||||||
this._page._frameManager.frameRequestedNavigation(payload.frameId);
|
this._page._frameManager.frameRequestedNavigation(payload.frameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onFrameNavigatedWithinDocument(frameId: string, url: string) {
|
_onFrameNavigatedWithinDocument(frameId: string, url: string) {
|
||||||
|
if (this._eventBelongsToStaleFrame(frameId))
|
||||||
|
return;
|
||||||
this._page._frameManager.frameCommittedSameDocumentNavigation(frameId, url);
|
this._page._frameManager.frameCommittedSameDocumentNavigation(frameId, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -633,7 +654,7 @@ class FrameSession {
|
|||||||
|
|
||||||
_onExecutionContextCreated(contextPayload: Protocol.Runtime.ExecutionContextDescription) {
|
_onExecutionContextCreated(contextPayload: Protocol.Runtime.ExecutionContextDescription) {
|
||||||
const frame = contextPayload.auxData ? this._page._frameManager.frame(contextPayload.auxData.frameId) : null;
|
const frame = contextPayload.auxData ? this._page._frameManager.frame(contextPayload.auxData.frameId) : null;
|
||||||
if (!frame)
|
if (!frame || this._eventBelongsToStaleFrame(frame._id))
|
||||||
return;
|
return;
|
||||||
const delegate = new CRExecutionContext(this._client, contextPayload);
|
const delegate = new CRExecutionContext(this._client, contextPayload);
|
||||||
let worldName: types.World|null = null;
|
let worldName: types.World|null = null;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user