使用firefox插件将firefox窗口带到前面

时间:2015-08-16 04:34:32

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

我希望在我的firefox addon .i上运行特定函数时关注firefox创建一个.vbs文件,该文件可以将firefox [置于顶部]然后使用{{执行该exe来执行1}}。喜欢这个

nsIProcess

它运行正常。但由于某种原因,我想直接从插件代码集中Firefox,而无需其他应用程序的帮助。我阅读了firefox 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); api,但我无论如何都找不到关注窗口。我问的是如何从插件代码中集中浏览器。

编辑..

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

windows

focusFirefox.vbs

1 个答案:

答案 0 :(得分:3)

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

如果我们只能执行SetForegroundWindow winapi方法会很容易,但调用此方法的过程必须具有用户关注点。如果没有,那么它什么都不做,所以这里有强迫它的方法:

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

此方法执行以下操作:

  1. 获取当前位于前台的窗口
  2. 如果找不到,则常规SetForegroundWindow将起作用,因此它会执行此操作并退出
  3. 如果前台窗口找到它,则获取其线程的id
  4. 然后将其与当前firefox(目标窗口线程)的线程ID进行比较
  5. 如果相同则使用常规SetForegroundWindow并退出
  6. 如果它不相同则使用AttachThreadInput将firefox的线程附加到当前前台窗口的线程中
  7. 如果步骤6中的调用失败,则它放弃并退出该函数,它会抛出错误
  8. 如果步骤6中的呼叫未失败,则会调用SetForegroundWindow,因为它现在可以正常工作
  9. 然后取消在步骤6中完成的附件,因此,如果您现在进行SetForegroundWindow调用,它将失败,因为它通常应该
  10. 我想不出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. 将窗口恢复正常(不是“始终在顶部”)
    7. 它有一个问题,SendInput中同义词鼠标点击的x和y坐标没有被尊重,这就是为什么我必须使用SetCursorPos,在未来的修订中,我想解决这个问题所以它不使用SetCursorPos因为移动用户鼠标对用户来说很烦人,但是这个好处远远超过了轻微的烦恼,因为它非常重要的是让窗口集中,我暂时接受这个{{1因为我做了“在偷窃之前恢复光标位置”,所以这里是:

      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); 什么都不做。

      经过测试,即使另一个窗口设置为“始终在顶部”,这也有效,但由于另一个窗口始终位于顶部,因此在关注此窗口后,这将是最顶层的窗口。