squirrel icon indicating copy to clipboard operation
squirrel copied to clipboard

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

Open shirok1 opened this issue 1 year ago • 4 comments

已经验证了通过类似 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 avatar Feb 26 '24 16:02 shirok1

`browser.h` 的内容
#import <AppKit/AppKit.h>
#import <ScriptingBridge/ScriptingBridge.h>


@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<BrowserWindow *> *) 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 <BrowserGenericMethods>

- (SBElementArray<BrowserTab *> *) 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 <BrowserGenericMethods>

- (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


shirok1 avatar Feb 26 '24 16:02 shirok1

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

groverlynn avatar May 24 '24 04:05 groverlynn

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

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

shirok1 avatar May 24 '24 04:05 shirok1

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

shirok1 avatar May 24 '24 05:05 shirok1