[NavigationScheduler] history.back/forward/go(n) calls don't coalesce per spec when queued synchronously
04c8ceb
NavigationScheduler는 WebCore에서 frame당 하나씩 존재하는 pending navigation 슬롯으로, m_redirect에 ScheduledNavigation 하나를 보관하다가 다음 task 경계에서 실행합니다. HTML 스펙은 history 이동을 top-level traversable 기준의 단일 session-history-traversal 큐에서 처리되는 task로 정의하며, 동기적으로 queue된 history.back/forward/go 호출은 어느 frame에서 발생하든 실제 navigation 전에 하나의 net delta로 합쳐져야 합니다. 그러나 WebKit에는 이런 큐가 존재하지 않았습니다. scheduleHistoryNavigation()이 호출될 때마다 schedule()이 실행되었고, 내부에서 cancel()로 기존 항목을 제거한 뒤 m_redirect를 교체하는 방식이었습니다. 두 번째 호출이 첫 번째 호출을 조용히 덮어쓰는 구조였습니다.
Source/WebCore/loader/NavigationScheduler.cpp
ScheduledNavigation::AccumulateResult ScheduledHistoryNavigation::accumulateHistorySteps(int additionalSteps)
{
if (!additionalSteps) // go(0)은 reload이며, delta가 아닙니다
return AccumulateResult::NotHandled;
m_steps += additionalSteps;
m_historyItem = nullptr; // fire() 시점에 target을 재산출합니다
return AccumulateResult::Handled;
}
void NavigationScheduler::scheduleHistoryNavigation(Frame& originatingFrame, int steps)
{
if (auto* topLocalFrame = topLevelLocalFrame(); topLocalFrame != &m_frame) {
originatingFrame.loader().completed();
cancel();
topLocalFrame->navigationScheduler().scheduleHistoryNavigation(originatingFrame, steps);
return;
}
if (m_redirect) {
auto result = m_redirect->accumulateHistorySteps(steps);
if (result == ScheduledNavigation::AccumulateResult::Handled) {
if (!m_redirect->steps()) // net delta == 0: 이동 없음
cancel();
return;
}
}
schedule(makeUnique<ScheduledHistoryNavigation>(steps));
}
이 fix는 accumulateHistorySteps virtual hook을 추가했습니다. 기존처럼 ScheduledHistoryNavigation을 교체하는 대신, 이미 pending 상태인 항목에 step 수를 누적하는 방식으로 변경된 셈입니다. 또한 subframe의 history 이동은 top-level LocalFrame의 scheduler로 전달되어, iframe과 main frame의 호출이 올바른 scope에서 하나로 합쳐지게 됩니다. 이제 back();forward()는 net delta가 0이 되므로 spurious navigation 없이 cancel()로 처리됩니다. back();back()은 -2로 누적되어 중간 항목을 건너뛰고 한 번만 실행되며, go(0)은 reload 의미론을 보존하기 위해 명시적으로 제외되었습니다.
Significance
이 변경은 WebKit이 cross-frame history navigation을 처리하고 합치는 방식 자체를 재구성합니다. 해당 경로는 어떤 document가 로드되는지, load event가 언제 발생하는지, back-forward list가 어떻게 진행되는지를 결정하는 privileged path입니다. iframe scheduler에서 top frame으로의 새로운 전달 경로, 그리고 same-document 이동을 둘러싼 step 누적 경계 조건은 security에 민감한 control plane에 도입된 새로운 로직입니다.
Audit directions
Aaaaaaaaaaaaaa Aa Aaaaaaaaaaa Aaaaaa Aaaaaaaaaa Aaaa Aaaaaaa Aa Aaaaaa Aaaaaaaaaaa Aaaaa Aaaa Aaa Aa Aaaa Aaa Aaa Aaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaa Aaaaa Aaaaaaaa Aaa Aaa Aaaaaa Aaaaaaaaa Aa Aaa Aaaa Aaa Aaaaa Aaaa Aaaa
Aaaaaaaaaaaaa Aaa Aaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aa a Aaa Aaaaaaaaaa Aaaa Aa Aaaaaa Aaaaaaa Aaaaaa Aaaaaaa Aaaaaaaaaaaaaa Aaa Aaaa Aaaaa Aaaa Aaaaaaaaaaaaaa Aaa Aaa Aa Aaa Aaaaa Aaa Aaa Aaaaa
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aa Aaa Aaaaaaa Aaaaaa Aaaaaaaaaa Aa Aaaaaaaaaaa Aaaa Aaaaaaaaaaaaa Aaaaa Aaaaa Aa Aaa Aaaaaaaaaaaa Aaa Aaaa Aaaa Aaaa
Aaaaa a Aaaaaaa Aaaaaaaaaaaaa Aaa Aaaaaaaaaa Aaaaaaaaaa Aaaa Aaaaa Aaaa Aaaaa Aaaa Aaaa Aaaaaaaaaa Aaaaaaaaa Aaa Aaa Aaaa Aaa Aaaaaa Aa Aaaaaaaaa Aaaaaaa Aaaaaaaaaaaaaa Aaa Aa Aaaa a Aaaa Aaaaaaaaa a Aa Aaa Aaa Aaa Aa Aaa Aaaaaa Aaaaaaaa Aaaaa
🔒New cross-frame forwarding and step-accumulation paths introduce several boundary conditions across the same-document/cross-document divide — audit directions included.
더 확인하려면 구독해 주세요