Firefox 60 隱私及生產力設定

自從 Mozilla 2017 年釋出 Firefox 57 ,毀掉分頁群組和其他所有有用的插件之後,我就先跳去用 Waterfox 了。

後來我就持續不定時觀察各種分頁群組插件的完成度,是否達到接近 Quicksaver 本來做的 TabGroups 插件,上次做的調查是在 tabHide API 釋出不久之後,當時最好的選項 Simple Tab Groups 都還不是很好,我就沒有跳槽。

Firefox Quantum 首次通過勉強可用的測試

近期開始重新測試 Simple Tab Groups, 發現作者釋出了一個基於 tabHide 的實驗版本,短暫測試之後覺得還算可用,就先把工作用的 Profile 匯入到 Firefox Quantum + Experimental Simple Tab Groups 。測試的結論:

  • Experimental Simple Tab Groups 十分穩定,不會遺失分頁(這個 profile 有 50~100 個分頁,通常每次會同時開啟 10~20 個),切換方式也和以前差不多,速度也很快,整體來說沒什麼大問題
  • UnloadTabs: Firefox Quantum 沒有提供修改分頁標籤外觀的 WebExtensions API ,因此需要透過奇怪的 NativeExt 修改 UserChrome 樣式來達成這個效果,有點麻煩,我還沒試
  • Firefox Quantum 沒有比 Waterfox 快(這個 profile 和我的另一個更多分頁的 Waterfox profile 相比,插件也不一樣)
  • Firefox Quantum 會使用比 Waterfox 更多的程序(通常 Waterfox 只有兩個),總和起來吃掉比 Waterfox 更多的記憶體(沒有經過科學測量,只是我偶爾打開系統監視器看)
  • Firefox Quantum 在 MacOS 睡眠的時候似乎仍然會不斷 allocate 更多記憶體,但可能是因為睡眠停掉了某些作業系統的機制,吃掉更多的記憶體沒有被壓縮或是釋放,經常睡眠一個晚上起來就發現系統沒有反應,好不容易打開 Firefox Quantum 發現數個 process 吃掉 5GB 的記憶體。Waterfox 也有這個問題但比較輕微。

總結是勉強可以接受繼續用,畢竟 Waterfox 的安全性還是令我有點擔心,感覺維護人力不是很夠,我也沒時間去看他們有沒有真的 backport 每一個 mozilla 對 Firefox 做的 security patch 。

轉移更多 Profile

原本除了 Firefox 之外,我還有數十個 Chrome 的 profile ,拿來瀏覽比較不重要的網頁(like Facebook, Netflix) ,因為 Chrome 的 profile 切換比 Firefox 方便非常多。

但最近碰巧發現,Chrome 支援一個 Battery Status API ,讓網站可以得知使用者目前裝置的電力狀態,並且 Chrome 不提供任何設定的方法關閉這個 API ,Firefox 則是曾經釋出但是後來停止支援這個 API 。從這一案例,我看 Chrome 應該還有很多類似的缺陷,因此開始考慮轉移一些 chrome profile 到 Firefox 。

但考慮到我未來還有很多需求建立很多個 firefox profile ,因此開始 survey 一些適合所有 profile 都設定的選項,舉例來說:

  • 停用 WebGL :我沒有需要在瀏覽器裡面玩遊戲或是開這類型的網頁應用程式,而且 WebGL 可以拿來做 fingerprinting
  • 停用 WebAssembly :我非常反對的標準,你要快速的應用程式應該乖乖去開發原生應用程式。並且目前 Javascript 提供一定程度的原始碼透明性(雖然很多會混淆,但認真一點的話還是可以理解高階行為),WebAssembly 會把網頁應用程式變成一個黑盒子
  • WebVR :預設是停用的,但也是我用不到的功能
  • 停用 EME :我只要在要看 Netflix 的那個 profile 打開就好,其他 profile 沒必要開
  • 等等, Chrome 更誇張,連 WebUSB, WebBluetooth 都有了

整體來說,我覺得現在的網頁平臺越來越多 feature creep ,這些標準的支持者經常打著「Universal/ubiquitous platform」的名號,使用「網頁有跨平臺、快速發佈、使用簡單的優勢,因此應該讓網頁平臺支援更多應用程式功能,方便使用者及開發者」類似的論調。將網頁平臺視為一個應用程式發佈的管道我並不反對,然而當這些應用程式能夠存取越來越多底層作業系統的功能的時候,我不禁覺得,你們只是想要偷懶不去開發原生應用程式才來支持 web 增加功能,然而這卻越來越將 web 變成一個「任意程式碼執行的管道」,也賦予了瀏覽器越來越多本來應該是桌面環境要做的事情,這個是明顯的 Inner-platform effect anti-pattern

總之我自覺身為一個 web 的使用者,為了自己未來的利益(更好用、更安全的應用程式和網頁)著想,應該要抵制這些功能。

回歸到技術的解決方案,我最後找到 pyllyukko/user.js ,集合了幾乎所有我要的選項。我使用他的 relaxed 版本的 user.js 為基礎來修改,關掉更多安全選項以便利使用。修改後的結果放進我自己的設定檔 repo

我第一個轉移的 chrome profile 就是其中一個主要拿來看 facebook 的 profile 。因為我的 Firefox profile 太多了,因此我每一個都要選不同的佈景主題,方便我切換過去的時候知道這是哪一個。(不過 addons.mozilla.org 網站上面的佈景主題大部分都很醜,像長輩圖那種,再不然就很中二,要找到好的不容易,而且網站上面也沒有提供什麼方式可以簡單找到高品質的佈景主題)

插件

在新的這個 firefox profile 裡面,我裝了這些插件:

  • Experimental Simple Tab Groups
  • Privacy Badger
  • HTTPS Everywhere
  • Link Cleaner
  • Octotree
  • Snooze Tabs
  • Social Fixer for Facebook: 可以幫 fb 貼文標已讀,就不會一直出現重複的貼文,還可以以關鍵字過濾貼文,和各種強大的客製化功能
  • Flagfox
  • Facebook Container
  • Firefox Multi-Account Containers

Multi-account Containers 基本上是通用版的 Facebook container ,但它不會在你點 fb 上面的連結的時候把連結複製到另一個 container 開啟,查了一下發現兩個是可以並存的,所以我就讓他們並存了。另外 Experimental Simple Tab Groups 也有一個功能是把特定分頁群組指派成特定 container ,只要是那個 container 的分頁都會在那個群組開啟,也可以設定正規表達式抓取新開啟的分頁進到 container ,但後來想想我應該不需要同 container 的分頁都集中在一個群組,所以我就把兩個功能分開用了(就是不特別設定 simple tab groups 裡面的正規表達式)。

未解決的可用性問題

雖然研究了這麼多,但整體而言 Firefox Quantum 對我來說還是有很多可用性問題,尤其是當我轉移越來越多 profile (不管是從 chrome 或是 waterfox )過去,問題會越來越嚴重:

為什麼我不使用 container 功能取代 profile ?

  1. 因為這樣我就沒辦法「只開啟一部分(特定用途)的分頁」,我是照用途來分 profile 的(工作、閱讀新聞、閱讀文章、開發程式、開發網頁程式、學習科學、看影片、聽音樂等等),這樣我每次在做一件事情的時候只要開啟一個 profile ,不用就可以關掉,如果全部都擠在同一個 profile 的不同 container ,那一次全部開啟就很吃資源。

  2. 因為這樣沒辦法「只在有需要的時候啟用插件」,插件的安裝是跨 container 的,越多啟用的插件也表示越吃資源。然而我只有在看 Netflix 的時候需要啟用 NflxMultiSubs 、看 FB 的時候啟用 Social Fixer ,工作的時候啟用 Jitsi Meetings ,安裝的更多插件是資源的耗用,也是額外不必要的安全風險。

  3. 「特定網站在特定 container 開啟」的功能,在你有同個網站的一個以上的帳號的時候不適用,比如說 Facebook container 還是沒辦法同時登入兩個不同的 facebook 帳號。

一次開啟多個 Firefox profile 的問題

如果我在 Firefox Quantum 一次開啟很多個 profile ,每個 profile 的 Firefox 應用程式 instance 的圖示,都是 Firefox ,所以我的 Dock 上面就會有 N 個一模一樣的 Firefox 圖示,切換的時候根本不知道哪個是哪個……以前我都是用 Firefox-on-OS-X Label 這個 XUL 插件讓不同 Firefox profile 的 instance 的圖示顯示不同的 label ,但 XUL 插件被 mozilla 毀了,所以。

相容性

歡迎來到 2018 年,今年世界上的人類們還沒解決不同瀏覽器之間的相容性問題——哦,不過最近幾年不是微軟的錯了,是 Google 的錯。

  • Firefox 無法使用 U2F 登入 Google 帳號,儘管已經支援 U2F 協定,而且可以在 Github 上面用 U2F 登入, fuck you, google
  • Firefox 無法使用 Google Hangout (很不幸我還是得用這個服務),儘管 google 的說明文件表示支援 firefox ,這明顯是個謊言兒, fuck you, google

總結

Firefox Quantum 對我來說還是有重大的可用性問題,因此短期之內應該還是維持「不重要的網站用 Quantum 開,主要的使用還是在 Waterfox 」。

參考

Feitian U2F NFC 和 macOS 上面 U2F 的現狀

最近取得一個 Feitian (廠牌)製造的 U2F Key ,除了拿來當作線上帳號二階認證的因子之外我還想要研究一下能不能拿它來登入 MacOS 、放我的 ssh key,研究了很久才發現 U2F 和放 ssh key 完全是兩個不同的功能。

有三個常見相關的功能:

  • U2F: challenge-response 式的二階認證標準
  • CCID: 可以產生和儲存 RSA 或是 Elliptic curve key 的智慧卡,這就是儲存 ssh key 或是 PGP key 需要的功能
  • TOTP: 用對稱式金鑰產生 one time password 的協定

像是 Yubikey 4 有標示支援 OpenGPG,應該就表示支援 CCID ,Yubikey 也要靠他們出的軟體來切換 U2F/CCID/TOTP 模式。

另外我原本擔心我的 Feitian U2F key 有沒有受到之前 Infineon 晶片有爆出 RSA key generation 弱點的影響,查了之後才知道這個弱點不影響 U2F 的安全性,只會影響智慧卡(CCID)的部分。

U2F

U2F 除了可以拿來登入線上服務之外也可以拿來登入作業系統,通常是透過安裝和設定 pam-u2f ,這個網路上教學很多這邊就先不寫。

相關專案:

CCID

有幾個和 CCID 相關的專案:

其他

Feitian U2F key 似乎可以切換成 CCID 模式,只是要用他們家奇怪的 windows 應用程式,現在手邊沒有 windows 所以就沒有測試。

然後雖然現在已經 Firefox 60 了,Google 帳號還是不支援在 Firefox 上面使用硬體金鑰當作第二階段認證因子…… fuck you google

參考

Using Alt+Arrow keys in vim inside tmux

tmux seems to send different key sequences to vim, so I had to add both to vimrc:

nmap :tabprev
" Alt+left arrow inside tmux
nmap ^[[1;3D :tabprev
" Alt+left arrow outside tmux
nmap ^[[1;9D :tabprev
nmap :tabnext
" Alt+right arrow inside tmux
nmap ^[[1;3C :tabnext
" Alt+right arrow outside tmux
nmap ^[[1;9C :tabnext

 

You’ll need to use Ctrl+v to input the keys to vimrc, see this article.

And iTerm2 needs to be configured, Left Alt Key (or right if you want) = Esc+

Relevant articles (not sure what their relations are):

反編譯、分析、閱讀 Android 應用程式原始碼,以 17直播為例

有些 Android 應用程式會要求過多的權限,如果不給它的話還不讓你使用應用程式,但明明這權限就很不合理啊。

以 17 直播為例,看個直播幹嘛要存取我的檔案?

https://dl2.pushbulletusercontent.com/N86YBB9oAlvoEPmGO6QyO9NJvSDoFD9c/Screenshot_20180225-141150.png

看了就很不爽,就是這樣的應用程式訓練出毫不眨眼就隨便給出權限的使用者,所以今天拿它來開刀。

找出顯示中的 Activity

首先用 adb shell dumpsys activity 瞭解一下目前是哪一個 activity :

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
  Stack #1:
    Task id #7379
    * TaskRecord{b876752 #7379 A=com.machipopo.media17 U=0 sz=1}
      userId=0 effectiveUid=u0a204 mCallingUid=u0a204 mCallingPackage=com.machipopo.media17
      affinity=com.machipopo.media17
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.machipopo
.media17/.StartActivity bnds=[48,156][126,234]}
      realActivity=com.machipopo.media17/.StartActivity
      autoRemoveRecents=false isPersistable=true numFullscreen=1 taskType=0 mTaskToReturnTo=1
      rootWasReset=true mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{822c1b8 u0 com.machipopo.media17/.activity.MenuActivity_V2 t7379}]
      askedCompatMode=false inRecents=true isAvailable=true
      lastThumbnail=android.graphics.Bitmap@80a2cec lastThumbnailFile=/data/system/recent_images/7379_task_thumbnail
.png
      stackId=1
      hasBeenVisible=true mResizeable=false firstActiveTime=1519539613444 lastActiveTime=1519539613444 (inactive for
 26s)

主要是看 Activities= 那一行,就知道目前的 activity 是什麼。

反編譯取得原始碼

我已經裝好了 Mobile Security Framework ,直接上傳 APK 檔案就可以反組譯,還會順便做一些其他分析。

到安裝 MobSF 的目錄底下有個 uploads 資料夾, APK 的 checksum 就是資料夾名稱,進去之後會看到幾個資料夾:

接下來的重點就是看 java_sourcesmali_source 這兩個資料夾了。

閱讀 Java 原始碼

打開 MenuActivity_V2.java ,往下捲一下很快就看到:

顯然就是檢查有沒有寫入外部儲存空間權限的函式,我猜測它的行為如下:

  1. 呼叫 android.support.v4 API ,檢查使用者是否已經授權存取外部儲存空間(269 行)
  2. 如果沒有授權的話,把字串 android.permission.WRITE_EXTERNAL_STORAGE 加進 arrayList 。也就是說,arrayList 裡面的字串代表應用程式還需要的權限。
  3. arrayList 的長度,設為 n2 ,然後回傳。

再找一下 F() 它的 caller 有誰,搜尋了一下,只有這個函式:

在 1410 行的 if 檢查了 this.F() 是不是回傳零,也就是「應用程式所需要的權限數量是不是零」,如果不是零的話,執行 1411~1414 this.n 的相關操作。

看到它在 1411 行檢查了 this.n 是否為空,如果不為空的話呼叫 this.n.dismiss() ,初步猜測 this.n 就是上面截圖中對話框的控制 class ,而 1411~1412 則是在檢查目前有沒有已經開啟的對話框,如果有的話,把它關閉 (dismiss) 。

在檔案開始處 MenuActivity_V2 class 的屬性型別宣告證實了我的猜想:

從 207 行,我們得知 this.n 的型別是 com.machipopo.media17.fragment.dialog.e ,看起來就是個對話框 (dialog) 。

至於對話框 class 要怎麼操作,現階段不是很重要,畢竟我們只需要把對話關掉(應該說是完全不要呼叫顯示對話框的函式)就好了。

關掉對話框有很多種方法,但由於我們實際上要修改應用程式的行為光是改 java code 是沒用的,需要修改 smali code ,因此我想還是先看看它的 smali code 長什麼樣子,再思考要怎麼修改比較方便。

閱讀 smali 原始碼

一樣在 uploads 資料夾裡面,可以找到 smali_source/com/machipopo/media17/activity/MenuActivity_V2.smali 這個檔案,也就是對應 MenuActivity_V2.java 的 smali 原始碼。

首先我們需要先找到 F() 的 caller ,也就是 onStart()

雖然說我根本就沒學過 smali (應該只有研究 Java VM 的人才需要瞭解 smali 吧),但稍微看了整個 smali 原始碼,還是可以看到一些規律,加上一些背景知識就可以看懂一段 smali code 的行為。

smali 看來還是蠻高階的東西,簡單搜尋了一下 onStart 字串,就找到這一段:

什麼 Singleton , AppLogic 的,跟 Java code 裡面的名字一樣,因此確定從 5621 行就是 onStart() 沒錯。

往下到 5648 行,就看到呼叫 F() 的指令:

5649 行的行為,猜測大概就是呼叫 F() ,而 F 會回傳一個整數 (I) ,這個回傳值會放在 {p0} 裡面。

5651 行把 {p0} 裡面的結果移動到 v0

5653 行是重點,判斷 v0 是否為零 (equal to zero, eqz) ,如果是的話 jump 到 :cond_81 ,如果不是的話繼續執行

以上 5649~5653 這段 smali code ,對應的 Java code 是 1410 行 if (this.F() != 0 && Build.VERSION.SDK_INT >= 23) { && 前面的東西。

瞭解了以上行為之後,看得出來,如果在使用者有給出所有權限的狀況下:

  1. F() 會回傳零
  2. 在 5653 的判斷會成功,直接跳到 :cond_81

因此 :cond_81 之後就是我們想要的行為(回歸正常操作),而 5653 ~ :cond_81 這段則會跳出對話框,不斷阻擋我們操作。

看一下 :cond_81 會做什麼:

5768~5772 對應到 Java code 1416 的 com.machipopo.media17.business.AppLogic.a().o((Context)this);

最後的 goto 則是 1418 行 if (com.machipopo.media17.business.AppLogic.a().b(.......

綜合以上判斷,我們需要修改的地方,就是把 5648 ~ :cond_81 這段行為,完全刪除,如此一來,無論 F() 的回傳值為何, 1416 行檢查權限成功之後的行為都會照常執行。

重新組裝 APK

關於整個反編譯和重新 build apk 的流程,可以參考 Huli 的這篇《人人都會的 apk 反編譯》。

用 apktool 反編譯原始碼(其實直接用 MobSF 反編譯出來的應該也可以用,但為了避免 apktool 沒辦法把 MobSF 反編譯的原始碼包回去,省點 debug 的力氣,還是就另外用一次 apktool),然後照著剛剛的規劃,刪掉 5648 ~ :cond_81 的東西。需要特別注意的是,用 apktool 反編譯出來的原始碼會跟 MobSF 的有點不一樣,主要是 symbol 名稱不同,像是 cond_81 可能會是其他數字,但程式碼的結構會是一樣的,修改的時候注意一下就可以。

修改過 smali code 之後,用 apktool b xxx -o new_17.apk 它包回 apk 檔案,

然後它就發生了錯誤…:

I: Using Apktool 2.2.3
I: Checking whether sources has changed...
I: Smaling smali folder into classes.dex...
base/smali/com/machipopo/media17/activity/MenuActivity_V2.smali[5659,0] Cannot get the location of a label that hasn't been placed yet.
Exception in thread "main" brut.androlib.AndrolibException: Could not smali file: com/machipopo/media17/activity/MenuActivity_V2.smali

不確定是什麼原因(畢竟我不是很懂 smali),我猜大概是中間刪掉的 code 有什麼重要意義,所以導致後面呼叫錯誤,但沒關係,還有其他方法可以修改行為:

直接在 5651 行它把結果存入 v0 之後,把 v0 覆蓋為零(5653 行是我加入的),至於問我我怎麼知道 const/4 v0, 0x0 這種寫法?我是往上稍微捲一下, 5637 行就有這種指令,我直接改了寫上 5653 行。

解決這問題之後,使用 apktool b 的時候還有遇到另一個錯誤,照著連結中的解法,成功封裝起來了。

接下來的步驟是 zipalign 打包好的 apk,然後用 apksigner 簽章(產生 keystore 的方法見這裡),方法可以直接參考 Google 的文件

安裝問題

接下來我就把 apk 放進手機裡,然後點開安裝,它卻只會跳出「套件剖析錯誤」的提示,從 adb logcat 看詳細錯誤看到: Parse error when parsing manifest. Discontinuing installation

查了一下,照著這篇改用 adb install 安裝就可以了。

成功

https://dl2.pushbulletusercontent.com/BxEUwLrfTKMNj8HUojseWOQgRLD2rRh9/Screenshot_20180225-225223.png

再也不會有惱人的提示框啦!

參考

學習 smali 語法: http://blog.csdn.net/wdaming1986/article/details/8299996

來龍去脈

其實 17 的 app 在更早以前的版本不只會要外部儲存空間的權限,應用程式一打開,就會向你要求麥克風、相機和簡訊的存取權限,這個是 2017 年 7 月左右版本的原始碼:

當時看這個 app 我就更不爽了,應用程式一打開什麼功能都還沒開始用就跟你要一大堆權限,不給權限的話還整個不給你用,真的是非常不負責任的開發者行為。

TabHide 之後的分頁群組插件調查

Firefox 59.0a1 之後新增了大家等待已久的 TabHide 功能,所以我就來重新調查一次有什麼插件是符合我需求的了。我的希望是儘量能夠逼近 Quicksaver 原版 Tab Groups 的功能。

Conex

Conex 好像是 addons.mozilla.org 上面蠻熱門的類 Tab groups 插件,但我實測之後發現:

  1. 他的每一個分頁群組會存在一個 contextualIdentities 容器 (container) 裡面,這不合我的用途,因為
    1. 我已經把我的不同用途的 cookie store 放到不同的 Firefox 的 profile 裡面了,我希望我的所有分頁群組共用一個 cookie store。
    2. 這行為和本來 Quicksaver 的版本不一樣。
    3. 而且 contextualIdentities 我之前檢閱完之後就覺得是個設計、模組化不良的 API
  2. 似乎是因為用了 containers 的關係,建立新容器、改名容器需要到 Firefox 的設定頁面裡面手動改。

Simple Tab Groups

  1. 切換不同群組的時候,不在顯示中的群組裡面的分頁會被卸載

後來我研究了一下,發現這個插件切換分頁群組的功能完全是使用 tabs.remove() 來達成的,也就是切換的時候會把分頁關掉,換回來的時候再打開。目前他們有個 issue 是要實作切換群組的時候分頁不會被卸載

Panorama View

這個插件目前最接近我的需求,不同分頁群組使用同一個 cookie store 和 Quicksaver 的版本相比,缺少幾個功能:

  1. 清單(專屬的清單頁面或是 context menu )顯示分頁群組和群組中的分頁:正在開發中
  2. 在不同群組之間顯示一樣的釘選分頁:有 PR

另外幾個是我覺得可以加的功能,我有空研究的話可以加加看:

  1. 指定不同分頁群組的顏色,這樣會達到類似 containers 的顯示效果,但是仍然共用 cookie store 。可能可以重複利用 VivaldiFox 這個插件的程式碼來作出不同顏色。

其他

另外,閒逛的時候還發現了幾個有趣的插件:

  • Social Fixer: 各種 Facebook 延伸功能,可以過濾貼文、標示貼文為已讀、移除特定區塊等等。但可惜不是開源的。
  • Auto Tab Discard : 簡單的定時 unload 分頁套件,我在 57 前就有用其他類似的插件。

更新 2/10

我發現 Simple Tab Groups 雖然缺少最重要的功能,但是程式庫、國際化和 UI 比 Panorama View 完整。

 

OpenVPN server settings in Asuswrt-Merlin

By default username/password authentication doesn’t seem to be enabled, choose Advanced Settings in the VPN server tab:

Down below, the Cipher Negotiation default will need to be disabled to work with OpenVPN clients <2.4.0 , which are quite common (Ubuntu 16.04 LTS still has OpenVPN 2.3.10 in package repo).

By default the VPN server also does not push routes, i.e. connection will be established but the client won’t change their default route to the tunnel interface created. This action could be added in the client ovpn file redirect-gateway def1, or the settings may be “pushed" by the server to the client by adding this server directive: push "redirect-gateway def1"

Update: I didn’t notice before, the “Direct clients to redirect Internet traffic” directive seems to do just that.

Firefox 57 毀掉分頁群組之後怎麼辦

眾所周知,預計 2017 年 11 月釋出的 Firefox 57 將移除所有舊版附加元件(使用 XUL/XPCOM/Addon SDK 的附加元件)的支援,只支援 WebExtensions 的附加元件。

如此重大的修改將會讓我失去 Firefox 對我來說最重要的分頁群組功能,原本分頁群組功能的開發者 Quicksaver 已經表示不會將原本的 Tab-Groups 插件移植到 WebExtensions API ,等於是宣判了這插件的死刑。

除了分頁群組之外,過去所有使用者熱愛的附加元件都將停止運作,各大討論版上面使用者哀嚎四起,還有 change.org 的連署,可惜沒過。

Mozilla 這個決定蠢斃了:

  • 大部分的使用者不使用附加元件沒錯,但不支援 XUL 附加元件會讓一大群 power user 和死忠粉絲背棄
  • 畢竟 Firefox 雖然比 Chrome 慢,但也只有 Firefox 可以讓你客製化到都認不出來,像我使用的 Firefox-on-OS-X Label 附加元件。 Chrome 你要做這 API ?還要先重新定義一個 Javascript 的界面、新增 API Permission 、一堆重複的工。Firefox?登登!Components.interfaces.nsIMacDockSupport !附加元件直接 tap into 原本就是負責處理這東西的內部界面,不需要重新定義界面巴拉巴拉。附加元件 15 行寫完收工。

好吧抱怨就此結束 :-p 我比較喜歡 Firefox 這樣可以自己自由 mod 的舊架構,而不是開發者爽開給你 API 才能 mod 的新架構。

身為一個分頁群組難民兼任自己要的功能自己寫的開發者,當然想要調查一下有什麼可行的解決方案可以拯救分頁群組功能,我整理出這些方法:

1. build 自己的 firefox fork ,把 legacy extension 直接包進去讓它可以用 legacy API

這會有點麻煩,每次釋出新版的時候要自己 patch 加入想要的 extension ,每個人想要的 extension 可能也不一樣,但這是保留原汁原味 legacy extension 唯二的方法。

挖了一下 Firefox 的 source code,原本內建在 Firefox 的 Panorama (就是分頁群組功能)在 codebase 裡面的這裡(應該是啦我有點不確定):mozilla-esr38/browser/components/tabview ,後來 tabview 被獨立出去成為 tabgroups extension ,所以理論上我只要把這個過程逆向操作,我就可以把現有的 extension 包回去 firefox 。但這 code structure 跟後來獨立出來成為 extension 的 tabgroups 長相實在差有點多,讓我有點搞不清楚到底當時是怎麼整理 codebase 獨立成 extension 的,這還需要更仔細的研究。

這麼做也有極限,因為 legacy API 和 extension 都不會再被維護,所以如果有新的 codebase 影響 legacy API 的運作,也不會被修正,legacy extension 也不會為了 broken API 去 workaround ,所以在更久的未來可能終究無法正常使用。

2. 啟動 legacy API 相容設定

猜測在 57 應該會有個 about:config 設定值可以允許 legacy extension 被安裝並且使用 legacy API 才是,雖然我還沒仔細去看這個設定值在哪裡。(等我找到再來更新這一段)

這個和方法1也有一樣的問題, legacy API 和 extension 都不會再被維護。

3. 使用 /firefox-tabgroups

這個 extension 本來是 tabgroups 的替代方案,也使用 legacy API, 但是和 tabgroups 不一樣的地方是作者 denschub 承諾說會支援新的 firefox ,雖然怎麼做、會有什麼功能他沒有說,但勢必一樣得受限於新的 WebExtensions API 。

4. 使用 contextualIdentities API 的相關插件

Firefox 前陣子新推出了一個 containers testpilot,基本上的概念是「在 Firefox 中透過不同容器來管理不同的線上身分,不論是線上購物、規劃旅遊行程、工作使用,更可以建立其他不同色彩與名稱的自訂容器。每個容器當中的 Cookie 會分開儲存」,它的目標使用情境看起來和分頁群組有些相關,因為分頁群組確實偶爾會被拿來這樣使用,但又有相當不同,containers testpilot 原本不是設計給「分頁管理」的情境下來使用的,所以沒有提供「顯示/隱藏」特定分頁的 API 。

有人去 containers 的 github 開了一個 issue 說,可不可以接受 containers 作為分頁管理工具的用途,獲得超多回應,畢竟現在這看起來是最有希望帶回分頁群組功能的道路了。

基於 contextualIdentities API  的插件目前有 eoger/tabcenter-reduxjonathanKingston/sea-containers ,這兩個是一開始就以 WebExtensions API 開發的插件,另外未來 denschub/firefox-tabgroups 應該也會使用這個 contextualIdentities API 。

contextualIdentities API 的問題

我去看過 contextualIdentities API 之後就覺得這真是個噁心霸道的東西,在棄用 legacy API 之後,插件沒辦法直接修改到 Firefox 的內部,所以照理來說 containers testpilot 是做不出來的,因為原本根本就沒有這種 WebExtensions API ——結果! Firefox 團隊就這樣直接幫 containers testpilot 開了後門,量身打造了一個 API 給他們——就是 contextualIdentities API 。

覺得很噁心,這不就剛好證明了能夠作出重大有意義的功能靠現有的 WebExtension API 根本做不到嗎?以前可以自己直接用 XPCOM ,現在還要先跟 Mozilla 開發團隊搞好關係才能在 core 塞進自己的插件需要的 WebExtensions API 。

撇開這個不說,這個 API 的設計看來也是一個 monolithic piece of poorly designed code 。正常來說 API 的設計應該是先暴露出最基礎的核心功能,然後由開發者自己組裝核心功能調用來作出有意義的插件吧?結果 contextualIdentities 這個 API 裡面直接把幾個在這個用途以外不相干的核心功能綁在一起暴露給 containers extension 使用:

  • 新增、刪除獨立的 cookie 儲存空間,一個新的 contextualIdentities API 會自動建立一個新的 cookie 儲存空間,完全沒有考量到獨立的 cookie 儲存空間可能會有 contextualIdentities 以外的用途,也沒有考慮到新的 contextual identity 想要分享現存 cookies 的狀況(共用一個 cookie 儲存空間)。
  • 更動右鍵選單項目,這個 API 直接自動新增了 “Open Link in New Container tab" 的項目。雖然我們已經有了 contextMenus API 可以新增右鍵選單項目,但顯然我們需要複製一份 code 給 contextualIdentities API 來讓使用者可以輕鬆地在新的 container 分頁裡面開啟連結,這樣複製一份 code 還可以減少 containers testpilot 插件開發者需要寫的 code ,非常棒。
  • 在 tab 頁籤上色的功能,在每個 contextual identity 裡面可以新增 tab ,在 contextual identity 當中新增的 tab 會自動被上色。顯然在分頁上色的功能只會有 containers 實驗會用到。
  • contextual identity 本身的頁籤顏色和圖示都只有固定的幾種可以選,圖示像是 cart, fingerprint, briefcase 等等,顯然不可能會有其他狀況下需要其他的圖示。

……正常的設計應該會是把前面三種功能分成三種不同的 API , 插件作者自己把這三種 API 結合在一起形成 contextual identity 的概念才對啊!!

另外,雖然有了 contextualIdentities API ,但還缺乏很多 API ,導致這些插件的功能還沒辦法實作出完全復刻原本 quicksaver 開發的 Tab Groups 的功能:

  • 如上所述,建立 contextual identity 的時候不能選擇共用 cookie store ,但 quicksaver 的版本裡面所有的分頁群組還是共用同一個 cookie store 的
    • 狀態:沒有查到相關的 feature request
  • 沒辦法選擇性只顯示部分的分頁(也就是只能顯示所有的分頁),quicksaver 的 tab groups 一次只會顯示一個群組。
  • 現在無法把某個 contextual identity 中的分頁移動到另一個 contextual identity ,只能關掉原本的分頁,然後開一個新的
  • 如果要使用類似 tree-style tabs ,把所有分頁和分頁群組放到側邊欄,那原本的橫向頁籤列應該要被隱藏,但現在沒有這樣的 API

相關資料、討論

個人感想

原本我以為沒希望了,更久以前我在搜尋的時候都找不到替代方案,幸好最近有 containers testpilot 所以才有這麼多延伸應用冒出來,有種燃起一線希望的感覺。但上面的那些 bug 大部分都還沒有被 mozilla 批准,我不太有信心 57 出來的時候分頁群組替代方案能夠完全提供舊有的功能,感覺上面當中一兩個還是很有可能會被 mozilla 拒絕…

更新

TabHide API 出爐之後我調查了幾個目前的插件,尋找符合我需求的,紀錄文在此