Browser 面板
Browser 面板在題目頁面裡呈現題目的應用程式。它有自己的網址列、自己的導覽按鈕、自己的一份歷史紀錄,這三者都不與你正在讀這一頁的真實瀏覽器分頁共用。
面板剛開啟時
網址列預先填入該題專屬的網址 https://challenge-<slug>.localhost/,其中 <slug> 是該題的識別代號。
在載入任何頁面之前,內容區中央顯示 Enter a URL and press Go。
題目 runtime 還沒就緒時,Go 按鈕停用,文字換成 …;就緒之後才變回 Go。而 runtime 由未就緒轉為就緒的那一刻,面板會自己對網址列上的網址發出第一次導覽——第一頁不需要你按 Go。
網址列左側永遠有一個 🔒 圖示。它是固定裝飾,不隨網址改變,也不代表任何連線狀態。
網址列
網址列是一個可自由編輯的文字輸入框。輸入任何網址後按 Enter 即可導覽;Enter 與 Go 按鈕送出的是同一個導覽事件,兩者可互相替代。欄位空白時顯示提示文字 https://challenge-…。
動手改它之前有兩個行為要先知道:
- 導覽進行中你打的字不會被蓋掉。 導覽還沒回來時改寫網址列,回應到達也不會覆蓋你輸入的內容。反之,你沒有改過它的話,導覽完成後欄位會被改寫成實際抵達的網址。
- runtime 未就緒時按 Enter 完全沒有反應。 輸入框不會停用,也不會跳出任何訊息,導覽在送出請求之前就被擋掉。螢幕上唯一的線索是 Go 按鈕顯示
…。
導覽按鈕
網址列左邊有三顆按鈕。它們只在桌機寬度出現——手機或窄視窗會把整組隱藏,只留下網址列與 Go。
| 控制項 | 做什麼 | 何時停用 | tooltip |
|---|---|---|---|
| ← 上一頁 | 重新顯示面板歷史紀錄中的前一筆 | 目前位置之前沒有紀錄時,包含第一頁剛載入完成的當下 | 可用時 Back,停用時 No earlier page in this tab |
| → 下一頁 | 重新顯示面板歷史紀錄中的後一筆 | 目前位置之後沒有紀錄時 | 可用時 Forward,停用時 No later page in this tab |
| ↻ 重新載入 | 重新抓取目前這筆歷史紀錄的網址 | 題目 runtime 未就緒,或上一次重新載入還沒回來;回應到達後恢復 | 固定是 Reload,停用時也不換字 |
上一頁與下一頁只看歷史紀錄,不看 runtime 是否就緒。所以 runtime 重建期間——也就是 Go 顯示 …、重新載入停用的那段時間——只要已經有歷史,這兩顆鈕仍然可按。按下去也只是把存下來的舊畫面搬回螢幕上。
導覽歷史的範圍
這份歷史紀錄屬於這個題目頁面的 Browser 面板。它不與其他題目共用,也不是真實瀏覽器的歷史:你瀏覽器自己的上一頁不會回到面板的前一頁。
- 上一頁與下一頁不會重新發出任何請求。 它們把先前存下來的畫面搬回顯示,因此 Network Traffic 記錄不會多一筆。
- 回上一頁之後再導覽到新網址,會丟掉「下一頁」方向的紀錄。 一旦往回走再往新的地方去,原本前方的紀錄全部作廢。
- 導覽到目前這筆紀錄的同一個網址時,是原地取代而不是新增一筆。 點一個指回本頁的連結、或對同一個網址再按一次 Go,歷史位置都不會前進——所以上一頁不會因此變成可按。真實瀏覽器在這裡的行為相反。
- 重新載入停在同一個網址時會留在原位。 網址沒變就原地更新該筆紀錄,「下一頁」方向的紀錄保留;若被導向到別的網址,就算一次新導覽並新增一筆。
- 上一頁與下一頁會作廢正在進行中的導覽。 已經送出的請求照樣送出、照樣記進流量記錄,但它的回應不會顯示,回應帶的
Set-Cookie也不會寫進面板的 cookie jar。
正在看的畫面若是用上一頁/下一頁叫回來的,內容上方會出現一條說明列 cached view · captured <時間> · press ↻ to refetch。如果從那個畫面被存下來到現在 session cookie 已經改變,同一條說明列會轉成警告色並補上 · session cookies have changed since this capture——螢幕上這一頁是在與現在不同的 cookie 之下被記錄下來的。
重新載入
重新載入一律以 GET 重送。它不會重送 POST 的本體,所以面板不會像真實瀏覽器那樣詢問你要不要重新送出表單。
它抓的是目前這筆歷史紀錄裡存的網址,不是網址列上的文字——你打到一半、還沒送出的網址會在按下它的時候被還原掉。
頁面內的連結與表單
頁面內的點擊與表單送出都會被面板攔下,改由面板自己重新發出請求。
| 在頁面裡做的事 | 面板送出什麼 |
|---|---|
| 點一個連結 | 一個全新的 GET,並帶上目前網址當作 Referer 標頭,可在流量記錄裡看到 |
點一個 href 為空或以 # 開頭的連結 | 什麼都不送:沒有請求、沒有流量記錄、沒有歷史紀錄 |
送出 GET 表單 | 把欄位接成查詢字串附在目標網址後面,因此網址列會出現 ?key=value |
送出 POST 表單 | 依表單的 enctype 組出 multipart/form-data 或 urlencoded 的本體發出請求;成功的回應和一般導覽一樣進入歷史紀錄 |
送出一個沒有 action 屬性的表單 | 目標取自網址列當下的文字,不是目前這一頁的網址。先改網址列再送出,表單就會送到被改過的那個網址 |
runtime 未就緒期間什麼都不會送出。即使螢幕上是用上一頁叫回來的舊畫面也一樣——點它的連結、送出它的表單,都不會產生請求。
面板自己維護一份 cookie jar,jar 裡有的 Cookie 會在後續請求自動帶回,並出現在 Network Traffic 面板顯示的請求標頭裡。但在 Python 與 PHP 題目上,進到這份 jar 的並不是題目原本送出的 Set-Cookie:面板解不開平台用來搬運這些標頭的格式,存進去的是一段無意義的字串。那段字串照樣會被送出、照樣出現在流量記錄裡,但題目那一端認不得它,所以在這裡登入之後狀態不會延續,而且全程沒有任何錯誤訊息。見疑難排解。
另外兩個行為會讓「你看到的」與「實際送出的」不一致:
- 連續的轉址會自動跟隨,最多五次。 整條轉址鏈只在最終網址留下一筆歷史紀錄。跟滿五次之後不會出現任何錯誤:第六個
3xx回應會被當成一般回應直接顯示(通常是空白頁)並寫進歷史紀錄,網址列停在發出該次轉址的來源網址。 - 快速重複導覽不會取消任何東西。 連按 Go 或快速連點多個連結,每一筆請求都照樣送出、照樣記進流量記錄,畫面只顯示最後一次導覽的結果。先前的回應是被丟棄,不是被中止。
回應怎麼呈現
HTML 回應顯示在一個 sandbox 的 iframe 裡。該 sandbox 給了 script 與表單,但沒有給同源存取,所以題目應用程式的 JavaScript 讀不到實驗室本身的 DOM、cookie 與 localStorage。面板有自己的 cookie jar,與你瀏覽器的 cookie 儲存區分開——要看它送出什麼請走流量記錄,不要用瀏覽器的開發者工具。
回應若不是 text/html 就完全不進 iframe,改以純文字區塊呈現。application/json 會先排版成縮排後的 JSON 再顯示。
導覽失敗時
導覽失敗時內容上方會多出一條說明列:Navigation failed — the challenge runtime did not respond。任何導覽失敗都會顯示它,不只是重新載入——點連結失敗一樣看得到。
除此之外什麼都不變。原本在螢幕上的那一頁維持原狀,網址列的文字也保持原狀,所以失敗看起來很像「按了沒反應」。那條錯誤列就是要找的東西。
runtime 若一直沒有回來,疑難排解有已知限制的完整清單與各自的處理方式。