rime / squirrel

【鼠鬚管】Rime for macOS
https://rime.im
GNU General Public License v3.0
4.6k stars 412 forks source link

在已有 app_options 的基础上提供根据当前浏览器页面进行条件判断的功能 #847

Open shirok1 opened 6 months ago

shirok1 commented 6 months ago

已经验证了通过类似 AppleScript 的进程间通信来实现的思路,不需要安装附加插件(除了 Firefox,后面会说明原因)

最简单的方法当然是直接调用 AppleScript 解释器:

(* 理论上适用于任何 Chromium-Based *)
tell application "Arc" to return URL of active tab of front window

(* Safari *)
tell application "Safari" to return URL of current tab of front window

Foundation 中已有 NSAppleScript,调用即可(但我没有找到“在切换 tab 时触发”等事件监听,所以可能需要轮询

也可以通过 ScriptingBridge 直接用 Objective-C 写逻辑:

#import <Foundation/Foundation.h>
#import "browser.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        BrowserApplication *browser = [SBApplication applicationWithBundleIdentifier:@"company.thebrowser.Browser"];
//        BrowserApplication *browser = [SBApplication applicationWithBundleIdentifier:@"com.apple.safari"];
//        for (BrowserWindow *window in [browser windows]) {
//            NSLog(@"window %ld: name is %@", [window index], [window name]);
//        }
        BrowserWindow *window = browser.windows.firstObject;
        // Chromium-Based 只有 activeTab,Safari 只有 currentTab,以此简单区分
        BrowserTab *tab = [window respondsToSelector:@selector(activeTab)] ? window.activeTab : window.currentTab;
        NSLog(@"active tab is %@ %@", tab.name, tab.URL);
    }
    return 0;
}

这个 browser.h 是我根据 Arc 和 Safari 原始导出的 protocol 修改合并而来的,导出 protocol 的命令是:

# 两个 CLI 都随 Xcode 安装
sdef /Applications/Arc.app/ > Firefox.sdef
sdp -fh -o Arc.h --basename 'Browser' Arc.sdef

如果有能避免维护一个 protocol 头文件的方法是否会更好?我没写过 Objective-C,网上搜索到的 id 类型似乎仍然需要“某处”有该方法约定存在,而不能完全省略 protocol 声明

Firefox 的问题是它的文档表明它需要从 document 属性获取 path 信息,但是从下面这张截图可以看到这个功能并没有做出来:

Screenshot 2024-02-27 at 00 29 31

bugzilla 页面 来看这个问题貌似从 Firefox 3 开始有人注意到现在都没人真正弄出来……这个页面靠后的帖子提到一个 workaround 是用 插件 将 URL(默认是只有域名部分,不过对匹配网站来讲应该完全足够)放到窗口标题。AppleScript 是能正常获取到窗口标题的,因此最后就能获得当前网站。

shirok1 commented 6 months ago
`browser.h` 的内容 ```objc #import #import @class BrowserApplication, BrowserWindow, BrowserTab; @protocol BrowserGenericMethods /// All below is Chromium-Only - (void) close; /// Close - (void) select; /// Select the tab. - (void) goBack; /// Go Back (If Possible). - (void) goForward; /// Go Forward (If Possible). - (void) reload; /// Reload a tab. - (void) stop; /// Stop the current tab from loading. - (NSString *) executeJavascript:(NSString *)javascript; /// Chromium-Only: Execute a piece of javascript. - (void) focus; /// Focus on a space. @end /// The application's top-level scripting object. @interface BrowserApplication : SBApplication - (SBElementArray *) windows; @property (copy, readonly) NSString *name; /// The name of the application. @property (readonly) BOOL frontmost; /// Is this the frontmost (active) application? @property (copy, readonly) NSString *version; /// The version of the application. - (id) doJavaScript:(NSString *)x in:(id)in_; /// Safari-Only: Applies a string of JavaScript code to a document. @end /// An application's window @interface BrowserWindow : SBObject - (SBElementArray *) tabs; - (NSString *) id; /// The unique identifier of the window. @property (copy, readonly) NSString *name; /// The full title of the window. @property NSInteger index; /// The index of the window, ordered front to back. @property (readonly) BOOL closeable; /// Whether the window has a close box. @property (readonly) BOOL minimizable; /// Whether the window can be minimized. @property BOOL minimized; /// Whether the window is currently minimized. @property (readonly) BOOL resizable; /// Whether the window can be resized. @property BOOL visible; /// Whether the window is currently visible. @property (readonly) BOOL zoomable; /// Whether the window can be zoomed. @property BOOL zoomed; /// Whether the window is currently zoomed. @property (copy, readonly) BrowserTab *activeTab; /// Chromium-Only: Returns the currently selected tab @property (copy) BrowserTab *currentTab; /// Safari-Only: The current tab. @end /// A window's tab @interface BrowserTab : SBObject - (NSString *) id; /// Chromium-Only: The unique identifier of the tab. @property (copy, readonly) NSString *title; /// Chromium-Only: The full title of the tab. @property (copy, readonly) NSString *name; /// Safari-Only: The name of the tab. @property (copy) NSString *URL; /// The url of the tab. @property (readonly) BOOL loading; /// Chromium-Only: Is loading? @end ```
groverlynn commented 3 months ago

輸入法進程接收到的客戶端是系統的輸入法服務生成的proxy,所以能有特別的輸入優先級,也因此無法使用其他API(相信蘋果有保護隱私的考慮在)。所以你說的從根本上就無法實現

shirok1 commented 3 months ago

輸入法進程接收到的客戶端是系統的輸入法服務生成的proxy,所以能有特別的輸入優先級,也因此無法使用其他API(相信蘋果有保護隱私的考慮在)。所以你說的從根本上就無法實現

我尝试手动赋予输入法进程「控制其他 app 权限」确实无法生效,如果真要实现可能需要一个 helper daemon 然后与其进行 ipc

shirok1 commented 3 months ago

另外如果都要单独的 helper daemon 了,那可能不如直接做个浏览器插件