Skip to content

Commit 7facd37

Browse files
Merge pull request #14 from ut-code/feature/Lv3
Lv3(display:noneの判定)追加
2 parents b71aa55 + ddf257f commit 7facd37

File tree

4 files changed

+94
-46
lines changed

4 files changed

+94
-46
lines changed

src/content.ts

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1-
// content.tsx
21
export {}
32

3+
function isDisplayed(el: Element | null): boolean {
4+
if (!el) return false
5+
if (!el.isConnected) return false
6+
7+
let current: Element | null = el
8+
while (current) {
9+
const style = getComputedStyle(current)
10+
if (style.display === 'none') {
11+
return false
12+
}
13+
current = current.parentElement
14+
}
15+
return true
16+
}
17+
418
// 複数の問題の「現在の値」をまとめて取得する
519
function getCurrentValues() {
620
// 問題1: テキストのチェック
@@ -13,9 +27,14 @@ function getCurrentValues() {
1327
// src属性そのもの(例: "img/star5.png")を取得。絶対パス化されるのを防ぐため getAttribute を推奨
1428
const val2 = el2 ? (el2.getAttribute('src') ?? '') : ''
1529

30+
// 問題3: 要素の表示状態チェック
31+
const el3 = document.querySelector<HTMLElement>('[data-check3]')
32+
const val3Visible = isDisplayed(el3)
33+
1634
return {
1735
p1: val1,
1836
p2: val2,
37+
p3Visible: val3Visible,
1938
}
2039
}
2140

@@ -31,30 +50,42 @@ const debounce = <T extends (...a: any[]) => void>(fn: T, delay: number) => {
3150

3251
const ports = new Set<chrome.runtime.Port>()
3352

53+
// 変更があった場合のみメッセージを送信する
54+
let lastJson = ''
55+
function sendMessage() {
56+
const values = getCurrentValues()
57+
const currentJson = JSON.stringify(values)
58+
if (currentJson !== lastJson) {
59+
lastJson = currentJson
60+
for (const p of ports) {
61+
try {
62+
p.postMessage({ type: 'DOM_VALUE_UPDATE', values })
63+
} catch {}
64+
}
65+
}
66+
}
67+
3468
chrome.runtime.onConnect.addListener((port) => {
3569
if (port.name !== 'sidepanel') return
3670
ports.add(port)
3771

3872
// 接続直後に現在の状態を送る
39-
port.postMessage({ type: 'DOM_VALUE_UPDATE', values: getCurrentValues() })
73+
const initialValues = getCurrentValues()
74+
lastJson = JSON.stringify(initialValues)
75+
port.postMessage({ type: 'DOM_VALUE_UPDATE', values: initialValues })
4076

4177
port.onDisconnect.addListener(() => ports.delete(port))
4278
})
4379

4480
// DOM変更を監視し、まとめて通知
45-
const notify = debounce(() => {
46-
const values = getCurrentValues()
47-
for (const p of ports) {
48-
try {
49-
p.postMessage({ type: 'DOM_VALUE_UPDATE', values })
50-
} catch {}
51-
}
52-
}, 200)
81+
const notify = debounce(sendMessage, 200)
5382

5483
const mo = new MutationObserver(() => notify())
5584
mo.observe(document.documentElement, {
5685
childList: true,
5786
subtree: true,
5887
characterData: true,
5988
attributes: true,
60-
})
89+
})
90+
// 定期的に送信
91+
setInterval(sendMessage, 300)

src/sidepanel/App.tsx

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ type CheckResult = { ok: boolean; details: string }
77
// 答えの定義
88
const ANSWERS = {
99
p1: "すばらしい。",
10-
p2: "./images/star5.png"
10+
p2: "./images/star5.png",
11+
p3Visible: false,
1112
}
1213

1314
function App() {
@@ -20,9 +21,10 @@ function App() {
2021
// イベントリスナー内で最新の step を参照するための Ref
2122
const stepRef = useRef(step)
2223
// 各ステップの初期値を保持するベースライン
23-
const baselineRef = useRef<{ p1: string | null; p2: string | null }>({
24+
const baselineRef = useRef<{ p1: string | null; p2: string | null; p3Visible: boolean | null }>({
2425
p1: null,
2526
p2: null,
27+
p3Visible: null,
2628
})
2729

2830
// state が変わったら ref も更新しておく
@@ -42,26 +44,17 @@ function App() {
4244

4345
port.onMessage.addListener((msg) => {
4446
if (msg?.type === 'DOM_VALUE_UPDATE') {
45-
const { p1, p2 } = msg.values
47+
const { p1, p2, p3Visible } = msg.values
4648
const currentStep = stepRef.current
4749

48-
4950
if (currentStep === 1) {
50-
// 初回ロード時の値をベースラインとして記憶
5151
if (baselineRef.current.p1 === null) {
5252
baselineRef.current.p1 = p1
5353
return
5454
}
55-
56-
// ベースラインから変化があった場合のみ判定
5755
if (p1 !== baselineRef.current.p1) {
5856
const isCorrect = p1 === ANSWERS.p1
59-
setResult({
60-
ok: isCorrect,
61-
details: isCorrect ? `一致: "${p1}"` : `不一致: "${p1}"`
62-
})
63-
64-
// 正解なら2秒後に次の問題へ
57+
setResult({ ok: isCorrect, details: isCorrect ? `一致: "${p1}"` : `不一致: "${p1}"` })
6558
if (isCorrect) {
6659
setTimeout(() => {
6760
setStep(2)
@@ -71,32 +64,47 @@ function App() {
7164
}
7265
}
7366
}
74-
7567

7668
if (currentStep === 2) {
7769
if (baselineRef.current.p2 === null) {
7870
baselineRef.current.p2 = p2
7971
return
8072
}
81-
8273
if (p2 !== baselineRef.current.p2) {
83-
8474
const isCorrect = p2.includes(ANSWERS.p2)
75+
setResult({ ok: isCorrect, details: isCorrect ? `一致: (${p2})` : `不一致: (${p2})` })
76+
if (isCorrect) {
77+
setTimeout(() => {
78+
setStep(3)
79+
setResult(null)
80+
baselineRef.current.p3Visible = p3Visible
81+
}, 2000)
82+
}
83+
}
84+
}
85+
86+
// 問題3のロジック
87+
if (currentStep === 3) {
88+
if (baselineRef.current.p3Visible === null) {
89+
baselineRef.current.p3Visible = p3Visible
90+
return
91+
}
92+
if (p3Visible !== baselineRef.current.p3Visible) {
93+
const isCorrect = p3Visible === ANSWERS.p3Visible
8594
setResult({
8695
ok: isCorrect,
87-
details: isCorrect ? `一致: (${p2})` : `不一致: (${p2})`
96+
details: isCorrect ? `要素が非表示になりました` : `要素はまだ表示されています`
8897
})
89-
// ※ここでさらに step3 へ遷移させることも可能
9098
}
9199
}
92100
}
93101
})
94102

95103
port.onDisconnect.addListener(() => {
96-
baselineRef.current = { p1: null, p2: null }
104+
baselineRef.current = { p1: null, p2: null, p3Visible: null }
97105
port = null
98106
setResult(null)
99-
setStep(1) // 接続が切れたら最初に戻す
107+
setStep(1)
100108
})
101109
} catch (e: any) {
102110
setError(e?.message ?? String(e))
@@ -109,7 +117,7 @@ function App() {
109117
if (port) {
110118
try { port.disconnect() } catch {}
111119
}
112-
baselineRef.current = { p1: null, p2: null }
120+
baselineRef.current = { p1: null, p2: null, p3Visible: null }
113121
}
114122
}, [])
115123

@@ -118,34 +126,36 @@ function App() {
118126
<div className="container">
119127
{error && <p style={{ color: 'red' }}>Error: {error}</p>}
120128

121-
{/* ステップ1の表示 */}
122129
{step === 1 && (
123130
<div>
124131
<h2>問題1</h2>
125132
<p>レビューの「とにかくひどい。」を「すばらしい。」に書き換えてみよう!</p>
126-
{result && (
127-
<div className={result.ok ? "result-ok" : "result-ng"}>
128-
<h3>判定: {result.ok ? '正解!' : '不正解'}</h3>
129-
<p>{result.details}</p>
130-
{result.ok && <p>次の問題へ進みます...</p>}
131-
</div>
132-
)}
133+
{result && <div className={result.ok ? "result-ok" : "result-ng"}><h3>判定: {result.ok ? '正解!' : '不正解'}</h3><p>{result.details}</p>{result.ok && <p>次の問題へ進みます...</p>}</div>}
133134
{!result && <p className="hint">ヒント:開発者ツールを使って、レビューに当たる要素を探してみよう。</p>}
134135
</div>
135136
)}
136137

137-
{/* ステップ2の表示 */}
138138
{step === 2 && (
139139
<div>
140140
<h2>問題2</h2>
141141
<p>星1の画像(star1.png)の <code>src</code> を書き換えて、星5 (star5.png) にしてみよう!</p>
142+
{result && <div className={result.ok ? "result-ok" : "result-ng"}><h3>判定: {result.ok ? '正解!' : '不正解'}</h3><p>{result.details}</p>{result.ok && <p>次の問題へ進みます...</p>}</div>}
143+
{!result && <p className="hint">ヒント:imgタグの src 属性を探そう。</p>}
144+
</div>
145+
)}
146+
147+
{/* ステップ3の表示 */}
148+
{step === 3 && (
149+
<div>
150+
<h2>問題3</h2>
151+
<p>一つ目のレビューを、CSSを使って非表示にしてみよう!</p>
142152
{result && (
143153
<div className={result.ok ? "result-ok" : "result-ng"}>
144154
<h3>判定: {result.ok ? '正解!' : '不正解'}</h3>
145155
<p>{result.details}</p>
146156
</div>
147157
)}
148-
{!result && <p className="hint">ヒント:imgタグの src 属性を探そう</p>}
158+
{!result && <p className="hint">ヒント:DevToolsのスタイルパネルで <code>display: none;</code> を追加してみよう</p>}
149159
</div>
150160
)}
151161
</div>

vulnerable-web/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ <h1 class="productName">
4646
<section class="reviewSection">
4747
<h2>レビュー</h2>
4848
<div class="reviewContainer">
49-
<div class="reviewBox">
49+
<div class="reviewBox1" data-check3>
5050
<img src="./images/reviewIcon.png" class="reviewIcon" />
5151
<div class="reviewContent">
5252
<p class="username">Username</p>
@@ -58,7 +58,7 @@ <h2>レビュー</h2>
5858
</p>
5959
</div>
6060
</div>
61-
<div class="reviewBox">
61+
<div class="reviewBox2">
6262
<img src="./images/reviewIcon.png" class="reviewIcon" />
6363
<div class="reviewContent">
6464
<p class="username">Username</p>

vulnerable-web/style.css

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,14 @@ main {
153153
gap: 20px;
154154
}
155155

156-
.reviewBox {
156+
.reviewBox1 {
157+
display: flex;
158+
background-color: white;
159+
border-radius: 10px;
160+
padding: 20px;
161+
box-shadow: 0 1px 6px rgba(0,0,0,0.1);
162+
}
163+
.reviewBox2 {
157164
display: flex;
158165
background-color: white;
159166
border-radius: 10px;
@@ -176,7 +183,7 @@ main {
176183
margin-bottom: 5px;
177184
}
178185

179-
.reviewText {
186+
.reviewtext {
180187
margin-top: 10px;
181188
color: #555;
182189
}

0 commit comments

Comments
 (0)