沒穿方服

索引

顯示╱隱藏內文
顯示具有 Firefox 標籤的文章。 顯示所有文章
顯示具有 Firefox 標籤的文章。 顯示所有文章

這個新功能是什麼

就是古老 IE 每點一個連結,轉到新頁面時會「嗒」一聲的音效。

Noise 轉為 web extension 後就一直沒做這功能,因為時機太難判斷,之前用 webRequest + webNavigation + History API 怎麼配都弄不好,尤其在一些 SPA 或嵌入很多廣告的網頁,會發瘋叫個不停。

直到 Firefox 147 支援 Navigation API,雖然使用情境仍複雜,但有比較清楚的介面定義,至少能安心實作。

「Navigate」的中文翻譯

另一個難題是 navigate 中文要翻成什麼。

一般翻成「瀏覽」、「導覽」,或「導航」,但缺乏上下文時都太曖昧。

參考 Windows 翻譯(檔案總管用的,IE 概念上就是 explorer 的延伸,所以音效也一樣)是用「開始瀏覽」和「導覽開始」,其實也看不懂。

Win 10 系統音效中的「開始瀏覽」

Firefox DevTools 的翻譯是「導航」:

Firefox 147 DevTools「主控台」面板訊息

我最後使用「瀏覽導航」,作為事件選項之一感覺還可以。

Navigate 具體發生時機

應用樣態多元,所以使用者其實不太能預期發生時機。

不如現在就安裝 Noise,搭配不同 navigationType 設定不同音效,在日常生活中慢慢認識它。

Noise 4.3 選項畫面「瀏覽導航」例子

示例(影片有聲音)

Noise 4.2.0 新功能「Multiple Sounds」

可以在事件上綁定「多個」聲音,可選擇是否隨機播放。

安裝 Noise:

註:套件裡面「沒有提供」影片中的聲音檔,要自己去生喔。

看似簡單的 CopyTabTitleUrl (web extension), 其實有選項「Use Extended Edit Mode 🧪」可以打開實驗性功能,但數量也太膨大了吧🧪🧪🧪,看它 wiki 上的 format 說明,到底是怎麼發展成這個小天地的?

選單成果

我沒有全部看懂,但湊出一個 format 能夠將 URL 中的 fbclid 和 utm_source 等追蹤碼去掉:
${linkSrcUrl.replaceAll('[?&](fbclid|utm_\w+)=[^&#]+','')}


設定介面也很難懂,瘋狂科學家風格

選項畫面

貼圖記錄,十年後可以回來看古代 UI。

分頁群組太過搶眼

Firefox 138 開始支援分頁群組(Tab Groups),預設顯示是在群組下面劃一條線,結果有點太醒目,造成無法一眼辨認「目前分頁」在哪。

自救

我已經好久沒用 userChrome.css,先貼圖,詳細記在後面。

userChrome.css 內容

用法是到 about:config 開啟 toolkit.legacyUserProfileCustomizations.stylesheets,然後到 Profile 資料夾自己新增 chrome/userChrome.css 檔案。

主要策略是用 CSS color-mix() 把預設背景色調暗。 另外有把關閉按鈕拿掉,內容如下:

2026-06-20: Firefox 152 更新了定義,新版為:

.tab-background {
  &:is([selected], [multiselected]) {
    background-color: color-mix(in srgb, var(--tab-background-color-selected) 70%, black) !important;
  }
}

.tabbrowser-tab[selected] .tab-close-button {
  color: white !important;
}
.tabbrowser-tab[selected]:not(:hover) .tab-close-button {
  display: none !important;
}

以下為舊版,已經沒用了。

.tabbrowser-tab[selected] .tab-background {
  background-color: color-mix(in srgb, var(--tab-selected-bgcolor) 70%, black) !important;
}

.tabbrowser-tab[selected] .close-icon {
  color: white !important;
}
.tabbrowser-tab[selected]:not(:hover) .close-icon {
  display: none !important;
}

開啟方式

  1. 確認 Firefox 版本在 135.0 以上(2025/2/4 釋出)
  2. 進入 about:config,找出 accessibility.typeaheadfind.wrappedSoundURL, 改成「beep」或一個指向 wav 檔案的 file:// URI(例如 file:///C:/%E9%9F%B3%E6%95%88/dorya.wav)即可

說明

Ctrl + F 文字尋找功能「已達頁尾,從頁首重新搜尋」這個狀態叫做 wrapped,目前各瀏覽器處理 wrap 的行為大致一樣,就是直接跳到另一端,缺點是不容易注意到。 相關討論 20 年前就存在,例如:

增加音效或許不是完善解方,但在很多情況下也夠用了。

可以用的值

新增的 accessibility.typeaheadfind.wrappedSoundURL 設定和 Accessibility.typeaheadfind.soundURL 類似,具體可用的值為:

  • 空字串(預設值)
    不會出聲,跟舊版行為相同
  • 字串 beep
    發出系統嗶聲,跟舊版「找不到指定文字」的音效相同
  • 字串 default
    發出古代 Firefox 的預設音效,很像屁聲,這不是我亂加的,是原始行為
  • 指向 WAV 聲音檔的路徑
    因為底層是用 nsISound 實作所以只支援 wav。 格式必須是 file URI,也就是以 file:// 開頭,不確定的話可以把音檔拖進瀏覽器,再複製網址列的文字即可

這個功能我等了七年

其實最早在 2008 年寫的擴充元件 Noise 裡就有實作這個功能,但隨著 Firefox 57(2017/11/14)改變架構讓 XPCOM 套件全滅,接下來就一直沒有替代方案,在長文研究時真的很痛苦。

2024 六月處理了一個 findbar bug,其實也是為這個音效做準備,這次一個 commit 就結案,只是等待 review 推進也是拖了一個月。

滑鼠點兩下選取一個字詞(word)的功能,在 Windows 下預設會包含字後面的空白,無視 DOM 元素邊界,還會把全形空白包進去。

平時不太會察覺,發現時往往不舒服,例如:

這個行為在 Chrome 也是一樣,但好像無解,只有 Firefox 可以用 about:config 改掉:

layout.word_select.eat_space_to_next_word 改成 false就可以了,參考。


其他跟選取空白有關的選項:

  • layout.word_select.stop_at_punctuation

    不要包含標點符號,預設 true。

  • layout.word_select.stop_at_underscore

    不要包含底線,預設 false。

  • editor.word_select.delete_space_after_doubleclick_selection

    編輯文字時,點兩下選取後再刪除,會把字前後的空白也刪掉,若不是點兩下選取就沒有效果,預設 false。
    參考 Bug 由來。

從來沒貢獻過 Firefox 本體,很久以前嘗試,光是 build 環境要裝 Visual C++ 等開發工具,太麻煩就放棄了。

現在我的環境有 WSL,又見 mozilla 文件已經寫得很詳細,就再來洗頭看看。
文件:Firefox Contributors’ Quick Reference

記錄在 Bug 1905331,功能很小,就是「尋找列」(Findbar) 找不到東西時會有一個 beep 音效,但 PDF 檢視時少了這個聲音,就把它加上去而已:

1905331 - Findbar in PDF viewer doesn't play "not found" sound

這個 bug 也是弄了 3 個月才結案,merge 後再跑 2 個月,11/26 才跟著 Firefox 133 正式上線。

安裝到能 build 起來已經不容易

官方說明(Firefox Source Docs)其實很不錯,更快的方式應該是找 mentor 或在 chat 問路,但我還是先自己試試。
文件:Getting Set Up To Work On The Firefox Codebase

Windows 下會教你用 MozillaBuild 安裝,會建立專用的 shell 設好獨立環境,缺點是會跟本來慣用的 shell 切開,有些東西像 git 也是另外裝一套,但確保跑得起來是第一優先啦,不做抵抗。

具體就是以下指令跑成功,該裝的就裝完了:
python3 bootstrap.py --vcs=git

但初期我還遇到 build script / 原始碼有些檔案沒處理好 python open 編碼的問題,也是追了一下。

然後 git pull 之後就能建自己的 Firefox 了:

./mach build ./mach run

在 WSL 跑 hg-git 慢到不行

WSL 遇過在 Windows 目錄跑執行檔會突然變超慢的問題(參考,把執行檔搬到 WSL 目錄竟然就沒事了),用前面裝的環境就剛好迎面撞上。

另 Firefox 原始碼還是用 Mercurial 做版本控制,如果要用 git 就要裝 hg-git 來中介,加上原始碼規模不小,結果一些 git 指令都要等非常久,久到中間一定要做別的事才行。

好用的 SearchFox

Mozilla 的程式碼搜尋,還支援 blame,重點是真的快。
https://searchfox.org/

哪那麼多 Findbar 呀

2008 年寫過 Firefox 3.0 的擴充套件,當時也改過 Findbar,記得 XUL 原始碼只有 gBrowser 裡面有 findbar,很輕鬆就能變造它的外觀和行為。

現在就不一樣了,tabbrowser(全域的 gBrowser)裡面有從某處初始化 findbar 的邏輯,widgets/browser-custom-element 裡面有視情況建立 FinderParent (remoteBrowser 時) 或一般 Finder (non-remote) 的分歧, 然後 PDF 的 viewer 模組裡又有自己的 findbar,這東西還有部分是從 pdfjs repo 不定期複製過來的,……各種各種。

這邊 trace code 和認識多年來的架構變更、引進概念(例如 fission 是什麼、modalHighlight 是什麼)也是個門檻。 還好 findbar 是相對冷門的功能,本身變動沒有很多。

Phabricator 協作平台

類似工具我之前主要是用 GitLab,還是第一次知道 Phabricator(音同 fabricator),配合 moz-phab 這個指令用起來很方便。

除了 Phabricator 本身有很多協作功能外,在 commit message 和遠端 branch 管理方面也可以省很多心力, 因為新的 branch 在用 moz-phab push 時就會自動生成對應的 Differential 頁面(跟本來的 branch name 已經無關),並把訊息塞到 commit message, 之後如果 review 來回要做 force push 也沒關係,只要在 commit message 保留 Differential Revision: .../D215196 這段文字,所有記錄就會回到同一頁,reviewer 也能看到你硬改了哪些東西。

Random fail 的測試

推上 Phabricator 的東西並不會跑所有測試,我這次接觸到的 CI 主要在程式要 land(類似所謂 merge 進主線)的時候,會由有權限的人 Pushing to Try,我才上去看測試死在哪,例如這具屍體。

之前工作時很頭痛的 random fail 問題,或稱 intermittent test / flaky test,就是平常跑都會過,偏偏就偶爾會死掉卡住 CI,這種要找出死因可能很花時間,於是常常不了了之。

這邊見識到 Firefox 的作法是有一個小組會去抓出案例,研究後登記起來,也提供查詢: Sheriffed intermittent failures。

然後跑測試時有一個 verify flag,對特定測試重複跑很多次,判斷是 intermittent 還是真的錯誤。

用 chat 求助

其實這個 bug 真正實作和討論的時間並不長,中間拖比較久是 reviewer 擺著,最後在 chat 求助才有人幫 land。

Extension API 的 Find API

原來跟瀏覽器的 findbar 根本沒關係呀!

之前一直想動 Findbar,很想從這裡下手,仔細研究才看懂走不通。

偵測「請輸入代號……」就自動送帳密,太過方便了,送出敏感資訊時,還是經過人手操作卡一道比較好。

執行畫面

Script 放在 Greasy Fork → PTT 半自動登入
原始碼也有放在 Github。


現有的自動登入方案

這邊說的 PTT 是指 https://term.ptt.cc/,採用修改版 PttChrome(登入後按右鍵「關於」就能看到)在網頁上提供類似純文字介面的操作,是官方維護、使用者眾的服務。當年上線消息:[公告] 使用瀏覽器直接連線至批踢踢。

BBS 沒有自動登入功能,早期都是靠連線軟體(例如 PCMan)的自動回應,看見「請輸入代號……」就自動回答。

當直接用瀏覽器上站變成主流,需求就落到擴充套件或 user script 上。 參考一些目前解法:

  1. term.ptt autologin by swstar / 2017

    實作是把 window.WebSocket 換掉以便在 open handler 送出帳密。

  2. term.ptt.cc 自動登入 by maple3142 / 2018

    除了登入,還提供自動跳過一些畫面的功能。 實作是捕捉 PttChrome 的 console 輸出(log 或 info),解析訊息判斷發生什麼事,再做反應。

  3. PttChrome+term.ptt.cc Add-on by alan23273850 / 2019

    功能眾多,自動登入只是其一。 實作是比對畫面 innerText 做自動回應。

    這幾支 user script 有一個共通限制,就是要把帳密直接寫在 script 裡才能使用。

  4. PTT Auto Login by c910335 / 2021

    這個不是 user script 而是 Chrome 擴充,利用瀏覽器的密碼管理員儲存/取得密碼,終於不必再寫死帳密了。

    但 Firefox 由於未支援 PasswordCredential,所以是不能用的。


新路數

「如果登入表單是一般 HTML <form>,應該就能沿用密碼管理員了吧?」

之前雖然想過,但一直沒試成功,最近用 <dialog> 避開跟 PttChrome 介面打架,場景乾淨之下就試出來了。

不過只有 Firefox 成功,Chrome 就無法讓密碼管理員跳出來(詢問要不要存密碼),所以 Chrome 的話會退回 PasswordCredential 解法。

實際使用上,只要做一次表單登入並儲存密碼,之後每次登入就只要按 Enter 即可。
如果臨時不想登入,也可以把對話框關掉,就回歸一般使用。


特色總結

  • 不要在 script 內文寫明帳號、密碼
  • 使用瀏覽器的密碼管理員
  • 支援 Firefox(測過版本 126.0.1)
  • 按下「送出」或 Enter 才真正執行登入

很基本的功能,按下鍵盤 ↓ 或 PageDown 的時候應該要捲動網頁吧? 為什麼沒捲呢?

因為 focus(焦點)不在你要捲動的東西上啊。 於是寫了一個套件 FocuScroll,可以設定快速鍵,按下去會修正焦點,讓捲動鍵發揮作用就是了。

主要精神是聚焦在可以捲動的主要內容上,我自己很常用,覺得對鍵盤瀏覽來說很重要。



舉例,有此現象的網頁:

  1. Javadoc - Overview (Java SE 10 & JDK 10 ) Javadoc,預期 PageDown 會捲動右邊的大框框,但實際不會

    現在版本是 Java 10,一進去就會自動跳到「搜尋框」所以 PageDown 不會捲動; Java 8 之前的版本則是因為焦點一開始在 <frameset> 最外層,而不是最大那一塊 <frame> 內容,所以也不會動。

  2. 內嵌 codepen 或 youtube 等等使用 <iframe> 的網頁,點了裡面按鈕,焦點就被吃進去,再按 PageDown 就不會捲動外面那層了。
    例如這邊放個跟捲軸有關的 youtube 影片,點播放以後 ↑ ↓ 就不能捲動這篇文章了。

原理和免責

如何找到真正要捲動的內容區塊? 其實我還沒想到很好的方法,現在是從畫面中央的元素開始,透過一些硬做的規則搜尋,細節會改來改去,但原則是要通過一些已知形式,都寫在原始碼的測試中。 可以想見一定有支援不了的網頁,那就再加測試、再修正吧。


Firefox 設定快速鍵的方法

藏在擴充套件管理的小齒輪圖示,裡面的「管理鍵盤快速鍵」功能。

Firefox「管理鍵盤快速鍵」功能

為什麼沒有人注意這個問題

鍵盤瀏覽比較會碰到這個問題,如果是用滑鼠滾輪,或筆電的觸控板滾動就不會遇到。

文件網站如 MDN、Microsoft Learn 有提供中文翻譯,有時候確實有幫助,像我就是看中文比較快。

但不是每個網頁都翻譯品質良好,差的時候,或者不確定差不差的時候就很尷尬,要切回原文確認,反而浪費時間。

有些領域則是已經習慣原文,但 google 搜到翻譯版連結,點進去反而陌生。

功能不親切的網站也是問題,例如 zh-hant.reactjs.org,要改語言就會離開目前主題(會跳去首頁)。

於是寫了一個瀏覽器套件 Delocalizer,它是 localize 的相反。

圖示是長得像丸子串的東西

提供一個工具列按鈕,「轉換目前頁面」會抓 URL 裡的 locale 部份(zh.xxx.com、xxx.com/zh/yyy),把它拿掉,再把 accept-language 請求標頭改成 en,讓網站吐英文。

Firefox 版工具列按鈕

也可以設定 xx 網站自動轉換,或者從 google 點連結才自動轉換。 以下是選項畫面:

選項畫面

隨意感想:

Firebug 2.0 即將於 6/10 登場,底層引擎換成 JSD2,可望解決這段時期常感受到的龜速、死雞、蟲蟲問題,感謝 Firebug Team 的努力!

Getfirebug blog 對每個 beta 發行都有相關介紹,爬文請見 Firebug Releases。


UI 改善

支援 Australis,把 Firebug 整合到新的 Firefox 選單按鈕,在不同 OS 也有與系統原生介面相合的設計了。

不過比起 Australis,更實用的還是其他細節的改善,例如「主控台」面板右上角多了一顆小箭頭,用途是「開關指令碼編輯器」,雖然跟右下的紅箭頭功能相同,但介面跟其他面板「開關側面板」的按鈕更一致,我現在幾乎不會按舊的按鈕了。

新版介面

舊版擷圖 舊版介面

主控台 log 合併的計數顯示,也改為放在左邊;

log 合併

舊版放在右邊,比較不容易看。
舊版的 log 合併


即時語法上色 (syntax highlighting)

Script 面板終於有顏色了!
(擷圖左邊是舊版,右邊是 2.0) Script 語法上色
Script 語法上色(逐步除錯中)

HTML、CSS 面板的「即時編輯」也一樣,按 Enter 換行時也會自動縮排,更像一般編輯器了。
CSS 語法上色
HTML 語法上色


Script 面板的 Pretty Print 功能

按一下「{}」按鈕就能把壓縮過的 JS 轉為易讀的格式。

Pretty print


檢測/修改函數的 return value

導入 JSD2 後,可以在 return statement 作逐步除錯了,如下圖在 return 行放中斷點,中斷後再 Step Over (F10) , debugger 會停在同一行,但右邊「監看」面板會得到 foo() 的回傳值,而這個值也可以當場編輯。

檢測 return

可以在 Getfirebug 的 DEMO 頁試試: Issue 6857: Give the ability to modify the returned value


自動完成

命令編輯器(Command Editor)可以按 Tab 自動完成。

指令碼編輯器的自動完成

設定中斷點條件時,也能自動完成該 scope 中的變數。

中斷點條件設定的自動完成


錯誤、中斷點相關改善

2.0 對「在所有錯誤中斷」和「在這個錯誤中斷」(就是放置中斷點)功能作了一些修正。

Thanks to JSD2 API Firebug could finally implement it correctly.

不是新功能,應該只是修正而已。

在所有錯誤中斷
放置中斷點

另外新增了「在例外中斷」功能,也能「忽略已捕捉的例外」忽略程式中已有 catch 區段的例外。

在例外中斷


Script 面板的其它改善

  • 滑鼠 hover 語句時顯示(運算值)預覽的行為,變得比較細緻。
  • Script 中的物件都能檢測,也能「在命令列中使用」(舊版曾有此功能,但常出錯)。
  • 滑鼠選擇起來的 expression 片段也能檢測,也能在命令列中使用。

HTML 面板可以使用正規表示搜尋

正規表示搜尋


以 JSON 格式匯出 cookie

註:需要自己在 about:config 打開 extensions.firebug.cookies.jsonClipboardExport 才能使用。

匯出為 JSON


Console API in Web Workers

Worker thread 中也能使用 console.log 等方法。


檢測 DOM 事件

繼 Firecookie 被整併後,EventBug 也變成 Firebug 的一部分了。

HTML 面板新增的「事件」側板會列出 DOM 節點上有效的事件監聽器(event lisenter), 顯示事件類型、函數名稱、是否 useCapture 等資料。

列出的監聽器,除了元素上用 addEventListener 註冊的「直接」監聽器之外, 若事件 bubble 上去有其它 target 監聽,就會以「監聽器 來自 div#foo」(原文 "Listeners from")型式列出。

另外還有註冊於 window、document 上的監聽器,因為無法在 HTML 面板找到元素,所以不管檢測什麼節點時都會列出,顯示為「其它監聽器 Window」(原文 "Other listeners for")型式。

其它監聽器

點函數名稱會跳到 Script 面板;也可以像 CSS 一樣,在名稱前面按一下就啟用/停用監聽器。


監聽器的 wrapped function 顯示

使用一些 JS library 監聽事件時,例如 jQuery 的 element.on('click', handler),實際綁在元素上的將不是 handler 本身,而是一個把 handler「包」起來的 wrapper 函數,事件發生時,再由 wrapper 呼叫 handler 並把結果回傳。

問題是我們除錯時,通常對 wrapper 函數沒興趣,「被包裹」的 handler 才是我們想看的東西, 這時就可以開啟「顯示被包裹的監聽器」選項,Firebug 就會連被包裹的函數也一起列出來了。

顯示 wrapped 函數
wrapped 函數

若 handler 是採用 jQuery 綁在上層元素,但只對特定 selector 起反應的 delegation 方式,Firebug 也會把「jQuery selector filter」標示出來。

jQuery selector filter


Firebug 2.0 只支援 Firefox 30 以上的版本,而 Fx 30 目前也在 beta,預定 6/10 釋出。 若想試試 Firebug 2,就安裝 beta channel 的 Firefox 30 吧。

移除 Web Developer 後發現少了「清除 domain cookie」功能,於是補寫一個 FireGestures script,設定為 ← ↓ → ↑ ↓ (LDRUD)(Cookie、上下為關閉)取代之。

先詢問再砍 結束時 alert 找不到 domain cookie 時

// Require Firefox 17
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Services.jsm");

var
  manager = Services.cookies,
  prompter = Services.prompt,
  hostname = gBrowser.currentURI.host,
  cookies = manager.getCookiesFromHost(hostname),
  cookie,
  dialogTitle = 'FireGestures script',
  kills = [];

while (cookies.hasMoreElements()) {
  cookie = cookies.getNext().QueryInterface(Ci.nsICookie2);
  if (cookie.rawHost === hostname || hostname.endsWith('.' + cookie.rawHost)) {
    kills.push(cookie);
  }
}


if (kills.length) {
  if (prompter.confirm(
        null,
        dialogTitle,
        [
          hostname,
          '移除 ' + kills.length + " 個曲奇?",
          "\n  " + kills.map(function (cookie) { return cookie.name; }).join("\n  ")
        ].join("\n")
      )) {
    kills.forEach(function (cookie) {
      manager.remove(
        cookie.host,
        cookie.name,
        cookie.path,
        false
      );
    });
    window.alert('已移除 ' + kills.length + " 個曲奇。");
  }
} else {
  prompter.alert(
    null,
    dialogTitle,
    hostname + "\n沒有需移除的曲奇。"
  );
}

沒有 LiveReload 也過得很好

「雙螢幕 + FireGestures 搖擺手勢(按住滑鼠右鍵再按左鍵)重新載入網頁」 —— 至少比按 F5 快多了。

目前環境

Windows 7 的 Firefox 21 beta + LiveReload Firefox extension,
Ubuntu VM 的 Rails + guard-livereload。

我的 LiveReload 大概已經一兩年沒辦法用了,I Don't Know Why,最近被問到才重啟調查,發現在瀏覽器直接測試 WebSocket(可以用 WebSocket.org | Echo Test 測)是可以和 VM 裡的 LiveReload server 來往的,那八成是瀏覽器的擴充套件出狀況。

惱人的 LiveReload Firefox 擴充套件

LiveReload 官網提供的 2.0.8 版 xpi 因為還沒包含 Pull Request #8,所以應該是不堪用的,此外還有按鈕無法移除、連線錯誤沒有警告(有時甚至毫無反應)等煩人問題。

另一關鍵是沒有選項可以調整連線的 host 跟 port,預設是 127.0.0.1 和 35729,我 VM 裡的 server 就不是用這兩個,當然連不上。

註:LiveReload 現在的設計是 server 在頁面插入 livereload.js,讓 client 從中取得 host 和 port。 所以也許是我的 guard-livereload 版本太舊(0.3.1),或者頁面需要另做修改。

最後對策是改 xpi 內容,讓它讀取 Firefox 自訂選項 extensions.livereload.fc. 的 host 和 port(fc 只是隨便定的名字),具體是在 global.js 的 beforeEnablingFirst 加入:

// Use custom preferences
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  .getService(Components.interfaces.nsIPrefService);
prefs = prefs.getBranch("extensions.livereload.fc.");
if (prefs.getCharPref('host')) {
  this.host = prefs.getCharPref('host');
}
if (prefs.getCharPref('port')) {
  this.port = prefs.getCharPref('port');
}

升級 Firefox 14 後的不慣

有天在網址列打字搜尋,沒出現想要的結果,就點網址前的 小地球 圖示——以前點它,就會把網址文字框「還原」為現在的 URL——結果點了沒反應?

Awesome bar 搜尋

於是陷入看不見 URL 的狀態…… 查查沒有解答,就不知道怎麼辦了。

(如果懶得看完全文,解答是:按 Esc 就好了)

一直忍到上週才死心面對,順便試試 Mozilla 的 Add-on Builder,結果完成一個小元件 Reset URL by Globe(未上 AMO,但 Builder 頁面即可下載 .xpi)。

Add-on Builder 初試樂趣

動手時間變很短,反而都在讀文件、想像、解謎; 寫寫直接在瀏覽器上測試,不用存檔也不用重開,感覺創造過程沒什麼干擾。

不過 Add-on SDK 已支援的東西還是跟傳統 XUL overlay 元件有點距離,寫複雜點的功能大概就沒這麼爽了。


準備開始,先了解問題

第一想知道 Firefox 為什麼改變。 很快查到 Site identity UI updates | Mozilla UX,原來 Firefox 14 把網址列的 favicon 改成一顆 site identity 按鈕——就是憑證合法時顯示綠色掛鎖,沒憑證時一律顯示小地球(其實我覺得比較像鳥頭)的按鈕—— 免得使用者看到奇怪的 favicon 誤以為是安全/不安全的網站。

要做 add-on? 初步想法

至於為什麼「點擊時復原」消失就沒查到了,說不定只是漏掉,回報一下就會加回去了;
但因為是玩 Add-on Builder 的機會,就沒多想,草草動手了。

猜測復原網址的實作應該還在,只是點地球的時候少了事件觸發而已。 所以只要在 click 事件加幾行 code 就行了吧。

先看 DOM 結構

跟改網頁一樣,想調整某個元件,總要先了解它的構造。 這時方便的工具是 DOM Inspector,只要先選 File → Inspect Chrome Document → 隨便選一個文件,再點工具列上的 Inspect 按鈕,就會開啟類似 Firebug 檢測器的功能,在瀏覽器視窗點想觀察的部份,節點就會在 DOM Inspector 中展開了。

File → Inspect Chrome Document → 隨便選一個文件
實際點選小地球,展開的是一個 xul:hbox 元素。
點到的元素(xul:hbox)在 DOM Inspector 中展開了

讀原始碼,看行為實作

再來就直接爬 code,不必把整個原始碼抓下來,利用 MXR (Mozilla Cross-Reference) 網站就行了。

搜尋過程省略,最後找到幾處:

  • browser.xul#550 的 textbox#urlbar 就是網址列所在,上面有個 ontextreverted 事件,看來就是「復原」功能。
  • urlbarBindings.xml#226 有前述事件 handler handleRevert 的實作。
    裡面的註解 //(前略)was scrolling when they hit escape 也透露一個我不知道的用法 —— 想復原 Awesome Bar 的輸入,只要按 Esc 就好了, 早知道這招,也許就不必寫這個 add-on 了……

開始寫啦……

第一次玩,亂寫一通,後來才發現驚人事實—— revision 是公開的啊! 每次亂存檔都會被別人翻到,真是一件很糗的事啊。

總之照著 SDK 文件的 guide 就能寫出能動的元件了。 具體也就是找到小地球,幫他綁上 click 事件,請他被按的時候 gURLBar.handleRevert 而已。

我主要碰到的問題是:不知道如何存取 XPCOM 的 nsIDOMWindow(好讓我直接取用 window.gURLBar)。現階段 sdk/windows 拿不到它,只好用 unstable 的 sdk/window/utils,兩個混在一起用,最後成果也實在不太靠譜。


記憶體用量 好像有點多?

因為文章寫得有點久,所以這元件也用了一個禮拜了。
由 about:addons-memory 看來,這支小小 add-on 經常性地佔用不少記憶體,有空要再檢查一下。

記憶體用量 5.14 MB

簡單報告翻譯中的 Firebug 1.11(目前 alpha 5)locale 新增部分。(這不是完整的新功能)


主控台 Performance Timing 呈現

window.performance.timing 是 W3C Navigation Timing API 提供的資料,從 Fx 7 就已存在,相對於在頁面寫 <script> 計時,這個是瀏覽器本身進行請求、回應、重新導向、DOM 整理等動作時,自行記錄的時間,比較準確可靠。

Performance Timing 擷圖

各項時間的 Description 可能比較難懂,詳細可見 W3C 原文的 Processing Model 部分,裡面還有圖解。

其他參考:作者 blog post、MDN 瀏覽計時說明、msdn 瀏覽計時。


命令列 help 指令

我是譯了才知道有 help 這指令。

help 的輸出


CSS 選擇器查詢面板

整併了 SelectBug 擴充套件,CSS 面板多了 Elements 側欄,方便由 CSS 再去找元素。

可用右鍵「取得符合元素」,當然也可手動輸入 selector

Grab and Drag 3.0.1 升級後發生設定跑掉等、有的沒的慘況,所以我一直卡在 2.8.2,
今天才看到 3.1.2 新版,總算把問題解了。


3.1 新功能主要有黑名單編輯(對某些網站停用 Grab and Grag)和更聰明的自動切換選項 ——
在文字上水平拖曳 → 進入「選取文字」模式;
在文字以外的地方拖曳 → 進入「Grab and Grag」(抓拖)模式
—— 這樣幾乎完全不必手動切換了,除了水平選字,其他拖曳都是 Grab and Grag 就對了。


不過碰上 link 佔整行、前後沒文字時,會因為 link 左右沒有「文字」空間可以點,形成水平拖曳也不能選取的狀態:

「檢視所有項目」連結,兩邊沒文字,選不到

我的解決方式是配合 FireGestures,自訂一個腳本手勢 gadGrabAndDragExtension.gadToggle();,再設成「搖擺手勢」,就能在任何位置「先按左鍵、再按右鍵」切換選取/抓拖模式了。

搖擺手勢設定

收到使用者對 Noise 的問題

為了等待某個詞的出現,我讓一個討論區,請一個外掛,不停地、不停地重讀。
假如能夠 find 到那個詞時,發出聲音通知我,

我就可以同時做別的事了。

解決辦法,在 Noise 設定這個聲音

DOMContentLoaded&gBrowser.contentWindow.find('WORD_I_CARE')

(事件類型是 window event)

每次 DOM 讀完後,便自動進行尋找(Ctrl + F),若有找到就發出聲音。

實際上要加入網址限制,免得每個網站都自動尋找:

DOMContentLoaded&(event.target.location.host==='www.forum.com'&&gBrowser.contentWindow.find('WORD_I_CARE'))

除了 host,也有其他屬性可用來設限制,參考 window.location (#properties) - MDC Doc Center。

設定畫面

也不必再打主控密碼了(輸入過的話)

這是 Firefox chrome url 用的 bookmarklet。

我註冊都沒在記密碼的。自動登入失靈的時候,只好打開 Firefox 的「已存密碼」然後找個地方(例如搜尋列)把密碼抄一遍,再複製、貼上表單登入。

要是哪天我被盜帳號,原因就是螢幕擷圖給別人,恰好密碼就在搜尋列吧。為甚麼這麼麻煩,都是「已存密碼」不能右鍵複製的緣故啊。

  1. 於是,把這支 bookmarklet 加入書籤 → 複製已存密碼(右鍵選單)
  2. 打開「已存密碼」(chrome://passwordmgr/content/passwordManager.xul)
  3. 點剛剛加入的書籤。
  4. 就可以開右鍵選單,複製或顯示密碼了。多選也有效。

原始碼拆開如下:

// 應於 chrome://passwordmgr/content/passwordManager.xul 執行
if( ! netscape.security.PrivilegeManager.isPrivilegeEnabled('UniversalXPConnect') || ! document.getElementById('signonsTree') ) {
 alert('操作失敗:\n\n請於下述 URL 執行這支 bookmarklet:\nchrome://passwordmgr/content/passwordManager.xul\n\n');
 return;
}

window.leqPasswordPicker = {

 clipboardHelper: Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper),

 init: function(){

   if( ! document.getElementById("leqPasswordPicker_menupopup") ) {

     var popupset = document.createElement("popupset");

       var menupopup = document.createElement("menupopup");
       menupopup.setAttribute('id', 'leqPasswordPicker_menupopup');

         var menuitem = document.createElement("menuitem");
         menuitem.setAttribute('oncommand', 'leqPasswordPicker.show();');
         menuitem.setAttribute('label', '顯示密碼');

         var menuitem2 = document.createElement("menuitem");
         menuitem2.setAttribute('oncommand', 'leqPasswordPicker.copy();');
         menuitem2.setAttribute('label', '複製密碼');

     menupopup.appendChild(menuitem);
     menupopup.appendChild(menuitem2);
     popupset.appendChild(menupopup);

     document.getElementById('signonsTree').getElementsByTagName('treechildren')[0].setAttribute('context', 'leqPasswordPicker_menupopup');
     document.getElementById('SignonViewerDialog').appendChild(popupset);
   }

 },

 show: function(){
   var selections = GetTreeSelections(signonsTree);
   if (selections.length > 0) {
     var txt = [];
     for (var s = 0 ; s < selections.length ; s++) {
       var i = selections[s];
       txt.push(
           '網 站:' + signonsTreeView.getCellText(i,signonsTree.columns[0])
         + '\n使用者:' + signonsTreeView.getCellText(i,signonsTree.columns[1])
         + '\n密 碼:\n\t' + signonsTreeView.getCellText(i,signonsTree.columns[2]) + '\n'
       );
     }
     alert( txt.join('\n') );
   }
 },

 copy: function(){
   var selections = GetTreeSelections(signonsTree);

   if (selections.length > 0) {
     var txt = [];
     for (var s = 0 ; s < selections.length ; s++) {
       var i = selections[s];
       txt.push( signonsTreeView.getCellText(i,signonsTree.columns[2]) );
     }
     this.clipboardHelper.copyString( txt.join('\n') );

     // 送一個 copy 事件,這段可以不用
     var evt = document.createEvent("HTMLEvents");
     evt.initEvent('copy', true, true);
     document.getElementById('signonsTree').getElementsByTagName('treechildren')[0].dispatchEvent(evt);
   }
 }

};

leqPasswordPicker.init();