요소가 가려졌다는 오류가 나오면 위에 덮인 팝업부터 찾기 쉽다. 하지만 숨긴 창에서는 화면 갱신을 기다리는 단계가 멈춘 경우도 있었다. 북극성에서는 같은 오류 이름만 보고 보호를 풀지 않고 어떤 단계에서 프레임과 위치를 기다렸는지 읽도록 진단과 입력 경로를 구분했다.

위치를 읽었다고 그 자리에 바로 눌러도 되지는 않는다

스크롤이나 대화상자 배치 직후 DOM의 위치 값과 실제 화면의 hit test가 잠시 다를 수 있다. page-operation은 목표의 사각형을 반복해서 읽고 painted frame의 안정이나 일정 시간의 위치 안정 조건을 본다. 무조건 일정 시간만 자고 같은 좌표를 누르는 방식과 다르다.

현재 코드는 requestAnimationFrame과 제한된 타이머 표본을 함께 사용한다. 프레임 두 번의 안정 조건과 별도의 위치 안정 대기를 구분하고 전체 대기에도 제한을 둔다. 이 대기를 통과해도 요소 연결·서명·보임·disabled를 다시 본다. 위치가 안정됐다는 사실이 현재 요소의 유효성 확인을 대신하지 못하기 때문이다.

진단에는 어느 대기에서 멈췄는지가 남는다

제한 시간이 끝나면 animation-frame-wait 같은 phase와 visibility·focus·viewport·bounds·목표와 blocker의 제한된 구조를 남긴다. 오류 이름 하나보다 구체적인 단서다. 입력 원문이나 페이지 전체 본문을 모두 로그로 보내지 않고 현재 실패를 설명하는 범위를 좁혔다.

숨김 상태라는 사실만으로 모든 오류를 같은 원인으로 단정하지는 않는다. 실제 overlay가 있을 수도 있고 문서가 바뀌었을 수도 있다. 다른 코드의 수리 분류가 이 진단을 역사적으로 사용했더라도 현재 자동수리 실행이 복원됐다고 설명하지 않는다. 이번 글은 실제 진단 형식과 현재 입력 검사를 읽은 기록이다.

사이트가 프레임에서 결과를 쓰는 경우도 시험했다

background-raf 시험은 iframe 안의 입력과 버튼을 만들고 버튼 뒤 결과 반영을 사이트의 requestAnimationFrame 콜백에 둔다. 창을 숨기고 throttling을 켠 상태에서 네이티브 입력·클릭을 한 뒤 결과와 commits가 한 번인지 본다. 화면 갱신이 없다고 같은 클릭을 더 보내는 해법을 시험하지 않는다.

이것은 제어된 소유 시험 페이지의 설계다. 실제 외부 사이트 전체의 숨김창 동작을 새로 측정한 것은 아니다. 과거 0.1.150 감사에는 hiddenFrameRaf 회귀 통과가 남아 있다. 이번 집필에서 그 시험과 현재 코드를 읽었고 원래 앱을 실행해 다시 통과시켰다고 말하지 않았다.

실제 덮개는 그대로 입력을 막는다

같은 시험은 뒤이어 화면 전체를 덮는 overlay를 만들고 ELEMENT_OBSCURED를 기대한다. commits는 여전히 한 번이어야 한다. 숨김 프레임 문제를 해결하면서 진짜 가림 검사를 완화하지 않았는지 보는 반례다. 정상 사례의 성공만으로 보호가 유지됐다고 추정하지 않는다.

현재 hit test도 사각형 안의 제한된 후보점마다 실제 elementFromPoint와 shadow tree를 따른다. 넓은 카드 중심에 다른 요소가 있어도 실제 노출된 버튼 점을 찾을 수 있지만, 덮인 노드를 강제로 보이게 만들거나 가림을 통과시키는 기능은 아니다. 위치·화면·입력 검증을 각각 남긴 이유가 여기에 있다.

입력 수명과 메모리 정책을 연결해서 읽는다

현재 드라이버는 입력 거래 동안만 프레임 제한과 포커스 모방을 조정하고 끝나면 원래 값을 돌리는 별도 경로를 갖는다. 관찰 중 대화상자 갱신을 기다리는 예외도 제한 시간 뒤 복원한다. 이 후속 구현을 무시하고 과거 문서의 항상 throttling=true라는 말만 현재 규칙으로 쓰면 부정확하다.

공유 캐시 모듈은 이런 프레임 조작을 구현하지 않는다. 선언된 보호 조건과 읽기 보관 정책만 시험한다. 나는 배경에서 편하게 작업한다는 목표가 실패 단계의 구분과 실제 가림 검사를 지우지 않게 하고 싶었다. 다음 글에서는 입력 동안 바꾼 설정을 어떻게 원래 상태로 돌리는지 더 안쪽을 설명한다.