250 고침 2: Android 에서 덮개가 뜨자마자 걷히던 것 - 덮개는 사용자가 옮길 때만 걷음 · 기록의 예외 이름 빼기
a4 의 932 기기 확인 (노트 10+): 작업 기록에 "웹 페이지를 열지 못함 (…): net::ERR_TUNNEL_CONNECTION_FAILED" 는 남는데
덮개가 보이지 않았다 (버그 수정).
- 원인: Android 는 본문 오류 (onReceivedError) 뒤에 엔진의 오류 페이지를 또 읽어 진행률이 0 → 100 으로 한 번 더 돈다.
"새로 읽기 시작 (진행률이 1 아래로)" 에서 덮개를 걷어, 뜨자마자 걷혔다. Windows (Edge) 는 이 단계가 없어 통과했다.
- 덮개는 사용자가 옮길 때만 걷는다: 주소 · 뒤로 · 앞으로 · 새로고침 · [다시 시도] (WebNav 를 감싸 알림 - _NavWatch).
덮개가 웹 뷰를 덮고 있어 그 밖의 길로는 옮겨지지 않는다.
- 덮개를 띄우면 작업 기록에 "DNS 안내 덮개를 띄움 (…)" - 기기에서 "띄웠는데 안 보임" 과 "띄우지 않음" 을 가리게.
- DNS 실패 글에 예외 이름 ("TimeoutException:") 을 빼고 이유만 (a4 932).
- 시험: Android 순서 (본문 오류 → 오류 페이지 진행률 0 → 100) 뒤에도 덮개가 남음. 실제 Edge 통합 시험 다시 통과.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
통합
parent
0b0f2617ae
commit
f7d73da301
|
|
@ -164,6 +164,7 @@
|
|||
"DNS 서버가 {0} 의 주소를 찾지 못해 {1}": "The DNS server couldn't find the address of {0}, so {1}",
|
||||
"DNS 설정: {0}": "DNS setting: {0}",
|
||||
"DNS 실패 ({0}): {1}": "DNS failure ({0}): {1}",
|
||||
"DNS 안내 덮개를 띄움 ({0} · DNS {1})": "Showed DNS notice over the page ({0} · DNS {1})",
|
||||
"DNS: 기기 DNS → 직접 지정 (8.8.8.8 · 8.8.4.4). 앱의 인터넷 연결 (브라우저 · 다운로드 · 업데이트) 은 이 서버로 주소를 찾습니다. 되돌리기: 환경 설정 > 네트워크 · DNS > 시스템 DNS (끄기) 회사 · 학교망처럼 8.8.8.8 이 막힌 곳에서는 닿지 않는다는 안내의 [시스템 DNS 로 바꾸기] 를 누르세요.": "DNS: device DNS → custom (8.8.8.8 · 8.8.4.4). The app's internet connections (browser · downloads · updates) now look up addresses with these servers. To undo: Settings > Network · DNS > System DNS (off) On networks that block 8.8.8.8 (such as company or school networks), press [Switch to system DNS] in the \"can't reach\" notice.",
|
||||
"DOC · DOCX · HWP · XLS · XLSX 를 PDF 로 바꿔 보기 (변환기를 받음)": "View DOC · DOCX · HWP · XLS · XLSX converted to PDF (downloads a converter)",
|
||||
"DoH 서버 ({0}) 가 HTTP {1} 로 답했습니다": "The DoH server ({0}) answered HTTP {1}",
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@
|
|||
"DNS 서버가 {0} 의 주소를 찾지 못해 {1}": "DNS サーバーが {0} のアドレスを見つけられないため、{1}",
|
||||
"DNS 설정: {0}": "DNS 設定: {0}",
|
||||
"DNS 실패 ({0}): {1}": "DNS 失敗 ({0}): {1}",
|
||||
"DNS 안내 덮개를 띄움 ({0} · DNS {1})": "ページに DNS 案内を表示 ({0} · DNS {1})",
|
||||
"DNS: 기기 DNS → 직접 지정 (8.8.8.8 · 8.8.4.4). 앱의 인터넷 연결 (브라우저 · 다운로드 · 업데이트) 은 이 서버로 주소를 찾습니다. 되돌리기: 환경 설정 > 네트워크 · DNS > 시스템 DNS (끄기) 회사 · 학교망처럼 8.8.8.8 이 막힌 곳에서는 닿지 않는다는 안내의 [시스템 DNS 로 바꾸기] 를 누르세요.": "DNS: 端末の DNS → 直接指定 (8.8.8.8 · 8.8.4.4)。アプリのインターネット接続 (ブラウザ · ダウンロード · 更新) はこのサーバーでアドレスを調べます。元に戻す: 設定 > ネットワーク · DNS > システム DNS (オフ) 会社 · 学校のネットワークなど 8.8.8.8 がブロックされている所では、接続できないという案内の [システム DNS に切り替える] を押してください。",
|
||||
"DOC · DOCX · HWP · XLS · XLSX 를 PDF 로 바꿔 보기 (변환기를 받음)": "DOC・DOCX・HWP・XLS・XLSX を PDF に変換して表示 (変換ツールをダウンロード)",
|
||||
"DoH 서버 ({0}) 가 HTTP {1} 로 답했습니다": "DoH サーバー ({0}) が HTTP {1} を返しました",
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@
|
|||
"DNS 서버가 {0} 의 주소를 찾지 못해 {1}": "DNS 服务器找不到 {0} 的地址,因此{1}",
|
||||
"DNS 설정: {0}": "DNS 设置: {0}",
|
||||
"DNS 실패 ({0}): {1}": "DNS 失败 ({0}): {1}",
|
||||
"DNS 안내 덮개를 띄움 ({0} · DNS {1})": "已在页面上显示 DNS 提示 ({0} · DNS {1})",
|
||||
"DNS: 기기 DNS → 직접 지정 (8.8.8.8 · 8.8.4.4). 앱의 인터넷 연결 (브라우저 · 다운로드 · 업데이트) 은 이 서버로 주소를 찾습니다. 되돌리기: 환경 설정 > 네트워크 · DNS > 시스템 DNS (끄기) 회사 · 학교망처럼 8.8.8.8 이 막힌 곳에서는 닿지 않는다는 안내의 [시스템 DNS 로 바꾸기] 를 누르세요.": "DNS:设备 DNS → 手动指定 (8.8.8.8 · 8.8.4.4)。应用的网络连接 (浏览器 · 下载 · 更新) 将用这些服务器查找地址。恢复:设置 > 网络 · DNS > 系统 DNS (关闭) 在公司 · 学校等屏蔽 8.8.8.8 的网络中,请点击无法连接提示中的 [切换为系统 DNS]。",
|
||||
"DOC · DOCX · HWP · XLS · XLSX 를 PDF 로 바꿔 보기 (변환기를 받음)": "将 DOC · DOCX · HWP · XLS · XLSX 转为 PDF 查看 (需下载转换器)",
|
||||
"DoH 서버 ({0}) 가 HTTP {1} 로 답했습니다": "DoH 服务器 ({0}) 返回 HTTP {1}",
|
||||
|
|
|
|||
|
|
@ -207,7 +207,13 @@ class DnsResolver {
|
|||
last = e;
|
||||
}
|
||||
}
|
||||
throw DnsException(trf('DNS 서버 ({0}) 에 닿지 않습니다: {1}', [c.label, last]), unreachable: true, server: c.label);
|
||||
// 작업 기록 · 화면에 예외 이름 ("TimeoutException:") 없이 (a4 932)
|
||||
final why = switch (last) {
|
||||
TimeoutException(:final message) => message ?? tr('답이 없습니다'),
|
||||
SocketException(:final message) => message,
|
||||
_ => '$last',
|
||||
};
|
||||
throw DnsException(trf('DNS 서버 ({0}) 에 닿지 않습니다: {1}', [c.label, why]), unreachable: true, server: c.label);
|
||||
}
|
||||
|
||||
Future<DnsAnswer> _udp(InternetAddress server, String name, int type) async {
|
||||
|
|
|
|||
|
|
@ -63,6 +63,53 @@ abstract class WebNav {
|
|||
Future<void> setJavaScript(bool on);
|
||||
}
|
||||
|
||||
/// 250: 사용자가 페이지를 옮길 때 (주소 · 뒤로 · 앞으로 · 새로고침 · 다시 시도) 알린다 - DNS 덮개는 이때만 걷는다.
|
||||
/// 진행률로 걷으면 Android 는 본문 오류 뒤에 엔진의 오류 페이지를 또 읽어 (진행률 0 → 100) 덮개가 뜨자마자 걷혔다 (932 노트 10+)
|
||||
class _NavWatch implements WebNav {
|
||||
final WebNav n;
|
||||
final void Function() onUserNavigate;
|
||||
_NavWatch(this.n, this.onUserNavigate);
|
||||
|
||||
@override
|
||||
Future<void> load(String url) {
|
||||
onUserNavigate();
|
||||
return n.load(url);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> back() {
|
||||
onUserNavigate();
|
||||
return n.back();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> forward() {
|
||||
onUserNavigate();
|
||||
return n.forward();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> reload() {
|
||||
onUserNavigate();
|
||||
return n.reload();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stop() => n.stop();
|
||||
@override
|
||||
Future<void> exportCookies() => n.exportCookies();
|
||||
@override
|
||||
Future<void> pauseMedia() => n.pauseMedia();
|
||||
@override
|
||||
Future<void> runScript(String js) => n.runScript(js);
|
||||
@override
|
||||
Future<void> clearData() => n.clearData();
|
||||
@override
|
||||
Future<Object?> evaluate(String js) => n.evaluate(js);
|
||||
@override
|
||||
Future<void> setJavaScript(bool on) => n.setJavaScript(on);
|
||||
}
|
||||
|
||||
/// 페이지의 모든 동영상 · 소리를 멈추는 스크립트
|
||||
const pauseMediaScript = "document.querySelectorAll('video,audio').forEach(function(m){try{m.pause()}catch(e){}})";
|
||||
|
||||
|
|
@ -168,6 +215,12 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
|||
/// [error]: 엔진이 알려 준 본문 오류. 프록시 터널 실패이고 DNS 서버에 지금 닿지 않는 중이면, 그 이름의 실패 기록이 없어도
|
||||
/// (Chromium 이 실패한 프록시를 기억해 다시 묻지 않는 때 등) DNS 때문으로 본다.
|
||||
/// 덮개는 여기서 걷지 않는다 (새로 읽기 시작 · [다시 시도] 에서) - Android 는 오류 뒤에 읽기 끝이 또 온다.
|
||||
/// 250: 사용자가 다른 곳으로 가거나 다시 읽음 - 덮개를 걷는다 (다시 실패하면 또 뜬다)
|
||||
void _userNavigated() {
|
||||
_loadStartAt = DateTime.now();
|
||||
if (_dnsBlocked != null && mounted) setState(() => _dnsBlocked = null);
|
||||
}
|
||||
|
||||
void _checkDnsBlocked({String? host, String? error}) {
|
||||
final h = host ?? Uri.tryParse(_url)?.host ?? '';
|
||||
if (h.isEmpty) return;
|
||||
|
|
@ -177,7 +230,10 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
|||
if (server == null && f != null && error != null && (error.contains('TUNNEL') || error.contains('PROXY'))) server = f.server;
|
||||
if (server == null) return;
|
||||
final next = (h, server);
|
||||
if (next != _dnsBlocked) setState(() => _dnsBlocked = next);
|
||||
if (next == _dnsBlocked) return;
|
||||
// 기기에서 "덮개를 띄웠는데 안 보임" 과 "띄우지 않음" 을 가릴 수 있게
|
||||
widget.c.note(trf('DNS 안내 덮개를 띄움 ({0} · DNS {1})', [h, server]));
|
||||
setState(() => _dnsBlocked = next);
|
||||
}
|
||||
|
||||
/// 250: DNS 실패면 웹 뷰 위에 덮는다 ([시스템 DNS 로 바꾸기] [다시 시도]).
|
||||
|
|
@ -253,11 +309,8 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
|||
onTitle: (t) => mounted ? setState(() => _title = t) : null,
|
||||
onProgress: (p) {
|
||||
if (!mounted) return;
|
||||
// 250: 새로 읽기 시작 - 지난 DNS 실패 화면을 걷고 시작 시각을 적어 둔다
|
||||
if (p < 1 && _progress >= 1) {
|
||||
_loadStartAt = DateTime.now();
|
||||
_dnsBlocked = null;
|
||||
}
|
||||
// 250: 새로 읽기 시작 - 시작 시각을 적어 둔다 (덮개는 여기서 걷지 않는다 - [_userNavigated])
|
||||
if (p < 1 && _progress >= 1) _loadStartAt = DateTime.now();
|
||||
setState(() => _progress = p);
|
||||
if (p >= 1) {
|
||||
_checkDnsBlocked();
|
||||
|
|
@ -272,7 +325,7 @@ class _BrowserPageState extends State<BrowserPage> with RouteAware {
|
|||
_canFwd = f;
|
||||
});
|
||||
},
|
||||
attach: (n) => _nav = BrowserPage.debugNav = n,
|
||||
attach: (n) => _nav = BrowserPage.debugNav = _NavWatch(n, _userNavigated),
|
||||
// 250: 엔진의 본문 오류 (Edge · Android). 읽기 끝 (진행률) 만으로는 Android 에서 덮개가 뜨지 않았다 (931, 노트 10+)
|
||||
onMainFrameError: (u, e) {
|
||||
if (!mounted) return;
|
||||
|
|
|
|||
|
|
@ -399,6 +399,12 @@ void main() {
|
|||
host.onProgress(1);
|
||||
await tester.pump();
|
||||
expect(find.text('DNS 서버 (192.0.2.1) 에 닿지 않아 example.org 을(를) 열 수 없습니다.'), findsOneWidget);
|
||||
// 932 (노트 10+): Android 는 본문 오류 뒤에 엔진의 오류 페이지를 또 읽는다 (진행률 0 → 100) - 덮개가 걷히면 안 된다
|
||||
host.onProgress(0.1);
|
||||
await tester.pump();
|
||||
host.onProgress(1);
|
||||
await tester.pump();
|
||||
expect(find.text('DNS 서버 (192.0.2.1) 에 닿지 않아 example.org 을(를) 열 수 없습니다.'), findsOneWidget);
|
||||
// DNS 와 상관없는 오류 (이름 기록도 없음) 는 엔진 화면 그대로
|
||||
await tester.tap(find.text('다시 시도'));
|
||||
await tester.pump();
|
||||
|
|
|
|||
Loading…
Reference in New Issue