Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- Fix: 一部のUI要素の色が正しく表示されない問題を修正
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1243)
- Fix: 「D」キーでダークモードを切り替える際にsyncDeviceDarkModeのチェックがバイパスされる問題を修正
- Fix: エラー画面で操作不能になることがあるのを修正

### Server
- Enhance: リモートノートクリーニングジョブのスキップ処理のパフォーマンス改善
Expand Down
48 changes: 35 additions & 13 deletions packages/frontend-embed/public/loader/boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@

// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
(async () => {
// renderError 自身が throw すると、それが unhandledrejection を再発火させて
// onunhandledrejection ハンドラから renderError が再呼び出しされる無限ループに陥り、
// メインスレッドを焼き尽くしてエラー画面が操作不能になる。
// それを防ぐための再入ガード。
let renderErrorRunning = false;

window.onerror = (e) => {
console.error(e);
renderError('SOMETHING_HAPPENED');
Expand Down Expand Up @@ -81,26 +87,42 @@
}

async function renderError(code) {
if (renderErrorRunning) return;
renderErrorRunning = true;
try {
await renderErrorImpl(code);
} catch (e) {
try { console.error('renderError failed', e); } catch { /* noop */ }
} finally {
renderErrorRunning = false;
}
}

async function renderErrorImpl(code) {
// Cannot set property 'innerHTML' of null を回避
if (document.readyState === 'loading') {
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
}

let messages = null;
const bootloaderLocales = localStorage.getItem('bootloaderLocales');
if (bootloaderLocales) {
messages = JSON.parse(bootloaderLocales);
}
if (!messages) {
// older version of misskey does not store bootloaderLocales, stores locale as a whole
const legacyLocale = localStorage.getItem('locale');
if (legacyLocale) {
const parsed = JSON.parse(legacyLocale);
messages = {
...(parsed._bootErrors ?? {}),
reload: parsed.reload,
};
try {
const bootloaderLocales = localStorage.getItem('bootloaderLocales');
if (bootloaderLocales) {
messages = JSON.parse(bootloaderLocales);
}
} catch { /* localStorage / JSON.parse の失敗は無視 */ }
if (!messages) {
try {
// older version of misskey does not store bootloaderLocales, stores locale as a whole
const legacyLocale = localStorage.getItem('locale');
if (legacyLocale) {
const parsed = JSON.parse(legacyLocale);
messages = {
...(parsed._bootErrors ?? {}),
reload: parsed.reload,
};
}
} catch { /* localStorage / JSON.parse の失敗は無視 */ }
}
if (!messages) messages = {};

Expand Down
56 changes: 42 additions & 14 deletions packages/frontend/public/loader/boot.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@

// ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので
(async () => {
// renderError 自身が throw すると、それが unhandledrejection を再発火させて
// onunhandledrejection ハンドラから renderError が再呼び出しされる無限ループに陥り、
// メインスレッドを焼き尽くしてエラー画面が操作不能になる。
// それを防ぐための再入ガード。
let renderErrorRunning = false;

window.onerror = (e) => {
console.error(e);
renderError('SOMETHING_HAPPENED', e);
};
window.onunhandledrejection = (e) => {
console.error(e);
renderError('SOMETHING_HAPPENED_IN_PROMISE', e.reason || e);
// e.reason は falsy 値 (0, '', null 等) で reject される可能性があるため
// `||` ではなく in 演算子で存在確認する
renderError('SOMETHING_HAPPENED_IN_PROMISE', 'reason' in e ? e.reason : e);
};

let forceError = localStorage.getItem('forceError');
Expand Down Expand Up @@ -128,26 +136,46 @@
}

async function renderError(code, details) {
if (renderErrorRunning) return;
renderErrorRunning = true;
try {
await renderErrorImpl(code, details);
} catch (e) {
try { console.error('renderError failed', e); } catch { /* noop */ }
} finally {
renderErrorRunning = false;
}
}

async function renderErrorImpl(code, details) {
// Cannot set property 'innerHTML' of null を回避
if (document.readyState === 'loading') {
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
}

// 既にエラー画面が描画済みなら、同じ id を二重に挿入しないよう抜ける
// (連発するエラーは console.error 済み)
if (document.getElementById('errorInfo')) return;

let messages = null;
const bootloaderLocales = localStorage.getItem('bootloaderLocales');
if (bootloaderLocales) {
messages = JSON.parse(bootloaderLocales);
}
if (!messages) {
// older version of misskey does not store bootloaderLocales, stores locale as a whole
const legacyLocale = localStorage.getItem('locale');
if (legacyLocale) {
const parsed = JSON.parse(legacyLocale);
messages = {
...(parsed._bootErrors ?? {}),
reload: parsed.reload,
};
try {
const bootloaderLocales = localStorage.getItem('bootloaderLocales');
if (bootloaderLocales) {
messages = JSON.parse(bootloaderLocales);
}
} catch { /* localStorage / JSON.parse の失敗は無視 */ }
if (!messages) {
try {
// older version of misskey does not store bootloaderLocales, stores locale as a whole
const legacyLocale = localStorage.getItem('locale');
if (legacyLocale) {
const parsed = JSON.parse(legacyLocale);
messages = {
...(parsed._bootErrors ?? {}),
reload: parsed.reload,
};
}
} catch { /* localStorage / JSON.parse の失敗は無視 */ }
}
if (!messages) messages = {};

Expand Down
Loading