在 Linux 中竊取 Firefox 的焦點
此文件先前位於 wiki 上
此頁面描述 Linux 上原生事件實作的一個重要組件 - 焦點維持。為了在 Firefox 中處理原生事件,Firefox 必須始終保持焦點。如果使用者決定切換到另一個視窗(這是可以理解的),Firefox 絕不能知道它失去了焦點。
解決方案概觀
基本概念
基本概念是介入 XLib (X-Windows 用戶端函式庫) 層和應用程式之間。X-Windows 通過非同步事件通知應用程式事件(使用者輸入、視窗被銷毀、滑鼠移動)。指示焦點喪失的 FocusOut 事件被丟棄。這個想法基於 Jordan Sissel 預先載入函式庫的實作,該函式庫覆寫了 XNextEvent - 請參閱 http://www.semicomplete.com/blog/geekery/xsendevent-xdotool-and-ld_preload.html。
擴充功能
這個簡單的實作在只有一個瀏覽器視窗時運作良好。當涉及多個視窗時,會出現幾個挑戰
- 即使可能會開啟新視窗,原生事件也必須繼續流向活動視窗。但是,大多數視窗管理器會將焦點給予新開啟的視窗。
- 視窗切換:當想要切換到另一個視窗時,必須移動焦點。這需要 WebDriver 的 Firefox 擴充功能和此組件之間的合作。
- 關閉視窗:當視窗關閉時,焦點必須移動到另一個視窗。根據設計,如果活動視窗已關閉,WebDriver 不保證任何事情 - 除非正在切換到新視窗。在這種情況下,必須特別小心。
與其他組件互動
基本概念不需要與 WebDriver 的其他組件互動。但是,當涉及多個視窗時 - 建立、切換或銷毀,此組件應該意識到這一點。無法追蹤新視窗的建立 - 因為它可能是許多操作的副作用。可以追蹤切換和關閉。
相關技術
為了理解此解決方案,應該熟悉 X-Windows 及其事件。GDK 事件處理迴圈的知識也很有用。
實作細節
所有這些都描述了 firefox/src/cpp/linux-specific/x_ignore_nofocus.c 中的程式碼。
共享函式庫
劫持事件是通過覆寫 XNextEvent 完成的。包含 XNextEvent 修改後實作的共享函式庫使用 LD_PRELOAD 載入。修改後的函式開啟 /usr/lib/libX11.so.6 並調用真正的函式。然後檢查真正的函式返回的事件(即真正的事件)。
識別事件
在基本概念下,FocusOut 事件將被簡單地丟棄。但是,視窗切換使情況複雜化。
資料結構
有一個全域資料結構,用於記住以下資訊
- 活動視窗 ID(如果目前有)
- 正在建立的新視窗的 ID(如果存在)
- 如果視窗切換正在進行中。
- 如果視窗關閉正在進行中。
- 焦點是否已給予另一個視窗,並且應該被竊取回活動視窗?
- 活動視窗是否已收到
FocusIn事件? - 我們是否將目前活動視窗設定為關閉操作的結果?
Firefox 啟動
FocusIn 事件到達,且活動視窗 ID 為 0。設定新的活動視窗。請注意,在建立主視窗期間,會建立另一個子視窗,並且會將 FocusOut 事件傳送到活動視窗。幸運的是,此 FocusOut 事件表示焦點將移動到子視窗(由 NotifyInferior 識別),因此這是允許的。
使用者已切換到另一個視窗
這由 FocusOut 事件指示,該事件的詳細資訊欄位既不是 NotifyAncestor 也不是 NotifyInferior。此事件被簡單地丟棄,並替換為 KeymapNotify 事件,該事件隨即被 GDK 丟棄。
正在建立新視窗
此條件由 ReparentNotify 事件識別。當發生這種情況時,new_window 欄位將設定為新建立視窗的 ID。後續的 FocusOut 事件將被允許 - 在新視窗建立期間,事件將照常流動(來自活動視窗的 FocusOut 事件、到新視窗的 FocusIn 事件、到新視窗的 FocusOut 事件以及到新視窗子視窗的 FocusIn 事件)。在新視窗的子視窗收到 FocusIn 後,將發出對 XSetInputFocus 的呼叫,以將焦點返回到活動視窗。
發生視窗切換
在視窗切換期間,事件將照常流動。當視窗的子視窗收到 FocusIn 事件時,視窗切換被視為完成。視窗切換通過識別檔案 /tmp/switch_window_started 開始。在此檔案中,寫入一個 switch: 字串,後跟一個視窗 ID(ID 僅用於偵錯目的)。這將把活動視窗 ID 更改為 0,狀態更改為「切換期間」。在切換期間(或當沒有活動視窗時),不會丟棄任何事件。
正在關閉視窗
與視窗切換非常相似(也通過讀取檔案來識別)。但是,它指示視窗正在關閉 - 如果已關閉,則不會發生焦點竊取。此外,正在識別 DestroyNotify 事件,以找出何時關閉活動視窗(由使用者顯式關閉或由其他一些不是顯式調用關閉的操作隱式關閉)。在這種情況下,活動視窗 ID 也將設定為 0。
重要連結
- Jordan Sissel 的原始 XSendEvent hack
- XLib 事件 和 XLib 程式設計手冊
- X 程式設計手冊 / 規格




