【问题标题】:bring firefox window to the front using firefox addon使用 firefox 插件将 firefox 窗口置于最前面
【发布时间】:2015-11-08 23:54:33
【问题描述】:

当特定功能在我的firefox addon 上运行时,我想关注 firefox。我创建了一个可以关注 firefox[置顶] 的 .vbs 文件,然后我使用 nsIProcess 执行该 exe。像这样

file.initWithPath("C:\\Users\\madhawax\\Documents\\focusFirefox.vbs");
var process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
process.init(file);
process.run(false, args, args.length);

它工作正常。但现在出于某种原因,我想直接从插件代码集中 Firefox,而不需要另一个应用程序的帮助。我读了 firefox windows api,但无论如何我都找不到聚焦窗口。我要问的是如何从插件代码中聚焦浏览器。

编辑..

这是我用来关注 Firefox 的代码。

focusFirefox.vbs

Set wshShell = CreateObject("WScript.Shell")
wshShell.AppActivate("Firefox")

【问题讨论】:

  • 如果您的 Firefox 处于活动状态,请执行 DOMWindow.focus()
  • @Noitidart 你说的if Firefox is active 是什么意思? .如果我澄清成像我打开记事本并在上面输入一些东西。我的firefox插件的一个功能得到执行。所以我想让插件专注于firefox。我想让firefox出现在前面。所以记事本将转到底部火狐浏览器
  • 如果 Firefox 的 PID 的进程是当前活动的进程。否则,您将不得不在 hwnd 上使用 SetForegroundWindow 的 WinAPI 方法(也必须确保将其最小化)。如果您的 pid 是当前活动的进程,那么 DOMWindow.focus() 将起作用。
  • ctypes 方法来聚焦进程窗口在这里可以看到:developer.mozilla.org/en-US/Add-ons/Code_snippets/… osx 缺少 x11 方法,你必须 setApplicationFront
  • @Noitidart 哇!我想这就是我需要的。我会检查一下并告诉你

标签: javascript firefox-addon firefox-addon-sdk jsctypes


【解决方案1】:

这些解决方案使用 js-ctypes winapi。这是一个 Windows 解决方案。 OSX 和 Linux 没有这个问题,那里很容易偷取焦点。

如果我们可以只做SetForegroundWindow winapi 方法会很容易,但是调用这个方法的进程必须有用户焦点。如果它没有,那么它什么也不做,所以这里是强制它的方法:

附加到线程方法(推荐方法)

此方法执行以下操作:

  1. 获取当前在前台的窗口
  2. 如果没有找到,常规的 SetForegroundWindow 将起作用,所以它会这样做并退出
  3. 如果前台窗口找到它,则获取其线程的 ID
  4. 然后将其与当前 firefox 的线程 id(即目标 windows 线程)进行比较
  5. 如果相同,则使用常规 SetForegroundWindow 并退出
  6. 如果不相同,则使用AttachThreadInput 将firefox 的线程附加到当前前台窗口的线程
  7. 如果第6步调用失败,则放弃并退出函数,并抛出错误
  8. 如果第 6 步中的调用没有失败,那么它会调用 SetForegroundWindow,因为它现在可以工作了
  9. 然后它会撤消在第 6 步中完成的附件,因此如果您现在进行 SetForegroundWindow 调用,它将像往常一样失败

我想不出AttachThreadInput 会失败的原因,但如果失败了,那是该方法唯一不可靠的部分。如果有人知道为什么会失败,请分享失败原因的解决方案。但是,我将SetCursorPos 方法留在下面作为紧急后备。

Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/ctypes.jsm');

if (ctypes.voidptr_t.size == 4 /* 32-bit */ ) {
    var is64bit = false;
} else if (ctypes.voidptr_t.size == 8 /* 64-bit */ ) {
    var is64bit = true;
} else {
    throw new Error('huh??? not 32 or 64 bit?!?!');
}

var user32 = ctypes.open('user32.dll');

var GetForegroundWindow = user32.declare('GetForegroundWindow', ctypes.winapi_abi,
    ctypes.voidptr_t // return
);

var DWORD = ctypes.uint32_t;
var LPDWORD = DWORD.ptr;

var GetWindowThreadProcessId = user32.declare('GetWindowThreadProcessId', ctypes.winapi_abi,
    DWORD, // return
    ctypes.voidptr_t, // hWnd
    LPDWORD // lpdwProcessId
);

var AttachThreadInput = user32.declare('AttachThreadInput', ctypes.winapi_abi,
    ctypes.bool, // return
    DWORD, // idAttach
    DWORD, // idAttachTo
    ctypes.bool // fAttach
);

var SetForegroundWindow = user32.declare('SetForegroundWindow', ctypes.winapi_abi,
    ctypes.bool, // return BOOL
    ctypes.voidptr_t // HWND
);

function forceFocus() {

    var hToDOMWindow = Services.wm.getMostRecentWindow('navigator:browser');
    if (!hToDOMWindow) {
        throw new Error('No browser window found');
    }

    var hToBaseWindow = hToDOMWindow.QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIWebNavigation)
        .QueryInterface(Ci.nsIDocShellTreeItem)
        .treeOwner
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIBaseWindow);

    var hToString = hToBaseWindow.nativeHandle;
    var hTo = ctypes.voidptr_t(ctypes.UInt64(hToString));


    var hFrom = GetForegroundWindow();
    if (hFrom.isNull()) {
        var rez_SetSetForegroundWindow = SetForegroundWindow(hTo);
        console.log('rez_SetSetForegroundWindow:', rez_SetSetForegroundWindow);
        return true;
    }

    if (hTo.toString() == hFrom.toString()) {
        console.log('window is already focused');
        return true;
    }

    var pid = GetWindowThreadProcessId(hFrom, null);
    console.info('pid:', pid);

    var _threadid = GetWindowThreadProcessId(hTo, null); // _threadid is thread of my firefox id, and hTo is that of my firefox id so this is possible to do
    console.info('_threadid:', _threadid);

    if (pid == _threadid) {
        var rez_SetSetForegroundWindow = SetForegroundWindow(hTo);
        console.log('rez_SetSetForegroundWindow:', rez_SetSetForegroundWindow);
        return true;
    }

    var rez_AttachThreadInput = AttachThreadInput(_threadid, pid, true)
    console.info('rez_AttachThreadInput:', rez_AttachThreadInput);
    if (!rez_AttachThreadInput) {
        throw new Error('failed to attach thread input');
    }
    var rez_SetSetForegroundWindow = SetForegroundWindow(hTo);
    console.log('rez_SetSetForegroundWindow:', rez_SetSetForegroundWindow);

    var rez_AttachThreadInput = AttachThreadInput(_threadid, pid, false)
    console.info('rez_AttachThreadInput:', rez_AttachThreadInput);
}

setTimeout(function() {
    forceFocus();
    user32.close();
}, 5000);

窃取然后恢复光标位置方法(不推荐的方法 - 不是万无一失的,因为它模拟点击 - 如果用户有全屏窗口并且链接位于位置 0,0,它可能会点击类似链接的东西窗口)

这是一个使用 js-ctypes 并且仅适用于 Windows 操作系统的解决方案,它执行以下操作:

  1. 将目标窗口设置为“始终在顶部”(但焦点尚未在窗口中)
  2. 获取当前光标位置
  3. 将光标位置设置在窗口的左上角
  4. 点击使其获得焦点
  5. 将光标位置恢复到原来的位置
  6. 将窗口设置回正常状态(不是“始终在顶部”)

它的问题是SendInput 中的合成鼠标点击的 x 和 y 坐标没有得到尊重,这就是我必须使用SetCursorPos 的原因,在以后的修订中我想解决这个问题所以它不会'不要使用SetCursorPos,因为移动用户鼠标对用户来说很烦人,但好处远远超过轻微的烦恼,因为给予窗口焦点非常重要,我暂时接受这种SetCursorPos方法,因为我做了一个“将光标位置恢复到窃取之前的位置”,所以这里是:

Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/ctypes.jsm');

function myFocus() {

    //////// START Ctypes DECLARES

    if (ctypes.voidptr_t.size == 4 /* 32-bit */ ) {
        var is64bit = false;
    } else if (ctypes.voidptr_t.size == 8 /* 64-bit */ ) {
        var is64bit = true;
    } else {
        throw new Error('huh??? not 32 or 64 bit?!?!');
    }

    var user32 = ctypes.open('user32.dll');

    var SetWindowPos = user32.declare('SetWindowPos', ctypes.winapi_abi,
        ctypes.bool, //return
        ctypes.voidptr_t, //hwnd
        ctypes.voidptr_t, //hWndInsertAfter
        ctypes.int, //X
        ctypes.int, //Y
        ctypes.int, //cx
        ctypes.int, //cy
        ctypes.unsigned_int //uFlags
    );

    var RECT = ctypes.StructType('_RECT', [
        {left: ctypes.long},
        {top: ctypes.long},
        {right: ctypes.long},
        {bottom: ctypes.long}
    ]);
    var LPRECT = RECT.ptr;
    var GetWindowRect = user32.declare('GetWindowRect', ctypes.winapi_abi,
        ctypes.bool, // return
        ctypes.voidptr_t, // hwnd
        LPRECT // lpRect
    );

    var SetCursorPos = user32.declare('SetCursorPos', ctypes.winapi_abi,
        ctypes.bool, // return
        ctypes.int, // x
        ctypes.int // y
    );

    var POINT = ctypes.StructType('_tagPoint', [
        {x: ctypes.long},
        {y: ctypes.long}
    ]);
    var LPPOINT = POINT.ptr;

    var GetCursorPos = user32.declare('GetCursorPos', ctypes.winapi_abi,
        ctypes.bool, // return
        LPPOINT // lpPoint
    );

    // send mouse stuff
    var ULONG_PTR = is64bit ? ctypes.uint64_t : ctypes.unsigned_long;
    var DWORD = ctypes.uint32_t;
    var MOUSEINPUT = ctypes.StructType('tagMOUSEINPUT', [
        {'dx': ctypes.long},
        {'dy': ctypes.long},
        {'mouseData': DWORD},
        {'dwFlags': DWORD},
        {'time': ULONG_PTR},
        {'dwExtraInfo': DWORD}
    ]);

    var INPUT = ctypes.StructType('tagINPUT', [
        {'type': DWORD},
        {'mi': MOUSEINPUT} // union, pick which one you want, we want keyboard input
    ]);
    var LPINPUT = INPUT.ptr;

    var SendInput = user32.declare('SendInput', ctypes.winapi_abi, ctypes.unsigned_int, ctypes.unsigned_int, LPINPUT, ctypes.int);

    var INPUT_MOUSE = 0;
    var MOUSEEVENTF_LEFTDOWN = 2;
    var MOUSEEVENTF_LEFTUP = 4;
    var MOUSEEVENTF_ABSOLUTE = 0x8000;
    // end send mouse stuff

    var HWND_TOP = ctypes.voidptr_t(-1); //ctypes.cast(ctypes.int(-1), ctypes.voidptr_t);
    var HWND_NOTOPMOST = ctypes.voidptr_t(-2);
    var SWP_NOMOVE = 2;
    var SWP_NOSIZE = 1;

    //////// END Ctypes DECLARES


    // pick a one of our navigator:browser firefox windows to focus
    var browserWindow = Services.wm.getMostRecentWindow('navigator:browser');
    if (!browserWindow) {
        throw new Error('No browser window found');
    }

    // convert our DOMWindow to a HWND
    var baseWindow = browserWindow.QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIWebNavigation)
        .QueryInterface(Ci.nsIDocShellTreeItem)
        .treeOwner
        .QueryInterface(Ci.nsIInterfaceRequestor)
        .getInterface(Ci.nsIBaseWindow);
    var hwndString = baseWindow.nativeHandle;
    var hwnd = ctypes.voidptr_t(ctypes.UInt64(hwndString));


    browserWindow.focus(); // this is important, withou this, i dont know why, but the window will not become "always on top" from the SetWindowPos code on the next line
    var rez_SetWindowPos = SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
    console.log('rez_SetWindowPos:', rez_SetWindowPos);

    var myRect = RECT();
    var rez_GetWindowRect = GetWindowRect(hwnd, myRect.address());
    console.log('rez_SetWindowPos:', rez_SetWindowPos);

    var myRectLeft = parseInt(myRect.left.toString());
    var myRectTop = parseInt(myRect.top.toString());
    console.log('myRect.left', myRectLeft);
    console.log('myRect.top', myRectTop);

    var myPoint = POINT();
    var rez_GetCursorPos = GetCursorPos(myPoint.address());
    console.log('rez_GetCursorPos:', rez_GetCursorPos);
    var myPointX = parseInt(myPoint.x.toString());
    var myPointY = parseInt(myPoint.y.toString());
    console.log('myPoint.x', myPointX);
    console.log('myPoint.y', myPointY);

    var rez_SetCursorPos = SetCursorPos(myRect.left, myRect.top);
    console.log('rez_SetWindowPos:', rez_SetWindowPos);

    // may need to wait for the window to come to top
    // send click - i dont know why but the x and y coords of these send clicks is not being respected, it is just clicking where the mouse is, so im having to use SetCursorPos as temp hack
    var js_pInputs = [
        INPUT(INPUT_MOUSE, MOUSEINPUT(myRectLeft, myRectTop, 0, MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE, 0, 0)),
        INPUT(INPUT_MOUSE, MOUSEINPUT(myRectLeft, myRectTop, 0, MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE, 0, 0))
    ];

    var pInputs = INPUT.array()(js_pInputs);

    var rez_SI = SendInput(pInputs.length, pInputs, INPUT.size);
    console.log('rez_SI:', rez_SI.toString());
    // end send click

    var rez_SetCursorPos = SetCursorPos(myPoint.x, myPoint.y);
    console.log('rez_SetWindowPos:', rez_SetWindowPos);

    var rez_SetWindowPos = SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
    console.log('rez_SetWindowPos:', rez_SetWindowPos);

    user32.close();

}
setTimeout(function() {
    myFocus();
}, 5000);

这是需要的方法,因为如果调用它的进程不是当前关注的进程,SetForegroundWindow 什么都不做。

经过测试,即使将另一个窗口设置为“始终在顶部”,这仍然有效,但由于另一个窗口始终在顶部,因此在聚焦此窗口后它将是最顶部的。

【讨论】:

  • 感谢您的详细回答..它工作得很好
  • @FastSnail 一定要使用第一种方法 :) 我在这里也学到了很多东西,所以谢谢 :)
  • @FastSnail 你必须启用开发者偏好 - developer.mozilla.org/en-US/Add-ons/… - 然后通过 Ctrl + Shift + J 使用浏览器控制台
  • @FastSnail 浏览器控制台显示所有内容。我听说有一种方法可以仅调试插件,我还没有尝试过,但是尝试转到 about:debugging 然后单击插件旁边的“调试”。
  • @FastSnail 它为我存在 - i.imgur.com/tOHnPen.png - 不过我使用的是 beta 频道,所以它可能是 Firefox 44+。但是调试特定的插件我很确定以前就在那里,也许去 addn manager 和 clck debug - i.imgur.com/uQd7zJs.png - ?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-20
  • 1970-01-01
  • 1970-01-01
  • 2010-09-05
相关资源
最近更新 更多