1

i want to focus firefox when a specific function run on my firefox addon .i created a .vbs file which can focus firefox[bring to the top] and then i execute that exe using nsIProcess.like this

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);

it works fine.but now for some reason i want to focus Firefox directly from addon code without help of a another application . i read firefox windows api but i coudn't find anyway to focus window.what i'm asking is how can i focus browser from addon code.

edit ..

here is the code i used to focus firefox .

focusFirefox.vbs

Set wshShell = CreateObject("WScript.Shell")
wshShell.AppActivate("Firefox")
Noitidart
  • 35,443
  • 37
  • 154
  • 323
Madhawa Priyashantha
  • 9,633
  • 7
  • 33
  • 60
  • Just do `DOMWindow.focus()` if your Firefox is active. – Noitidart Aug 16 '15 at 04:46
  • @Noitidart what do you mean by `if Firefox is active` ? .if i clarify imaging i have open notepad and type something on it .and a function of my firefox addon get executed .so i want addon to focus firefox .i want firefox to come to the front .so notepad will go to the bottom of the firefox browser – Madhawa Priyashantha Aug 16 '15 at 04:52
  • Like if the process with the PID of Firefox is the currently active process. Otherwise you'll have to use WinAPI method of `SetForegroundWindow` on the hwnd (will have to eunsure unminimized it too). If your pid is the currently active process, then `DOMWindow.focus()` will work. – Noitidart Aug 16 '15 at 05:09
  • ctypes methods to focus process window are seen here: https://developer.mozilla.org/en-US/Add-ons/Code_snippets/Finding_Window_Handles#Windows the x11 method is missing for osx you have to `setApplicationFront`. – Noitidart Aug 16 '15 at 05:11
  • @Noitidart wow !.i think that's what i need.i will check little bit and tell you – Madhawa Priyashantha Aug 16 '15 at 05:14
  • Cool man let me know if you need help ive done all of this before and have codes laying around. Don't use exe just to focus a non-active app, its overkill, use js-ctypes. And if your app is already focused, then juse `DOMWindow.focus()` to focus the right window. With js-ctypes its very easy to go cross platform too. – Noitidart Aug 16 '15 at 05:27
  • @Noitidart i tried it but i have a problem .i set 6 second delay to my firefox focus function to run and then i focus another application .after 6 seconds ,when focus function get executed, in taskbar i can see firefox icon get flashed .but it doesn't come to the front .do you know why ?? here is a gif show my problem look at the firefox icon on taskbar . http://i.imgur.com/blABwUZ.gif – Madhawa Priyashantha Aug 16 '15 at 05:42
  • @Noitidart i run [this code](http://pastebin.com/cBYuvW6D) in scratchpad in browser mode also .but problem persist . – Madhawa Priyashantha Aug 16 '15 at 06:00
  • Jump onto mozIRC jsctypes channel we'll help you out. irc://moznet/jsctypes or if you dont have irc client then use online irc client mibbit: https://client00.chat.mibbit.com/?url=irc%3A%2F%2Firc.mozilla.org%2F%23jsctypes – Noitidart Aug 16 '15 at 06:04
  • Ah I figured out the issue. So WinAPI only allows `SetForegroundWindow` to be called by the currently active app. So if you focus something else, you have to make that guy call it. Let's work on figuring out how to activate self even if its not the active window. What was the code you used in your exe? – Noitidart Aug 16 '15 at 06:14
  • @Noitidart i added the code in my exe.but it's actually not a exe but a vbs . – Madhawa Priyashantha Aug 16 '15 at 06:21
  • Did your code work to focus firefox? Make your code do it after a delay. And during that delay focus something else like notepad. – Noitidart Aug 16 '15 at 06:37
  • I posted asking for help here: http://stackoverflow.com/questions/32032548/steal-focus-with-setforegroundwindow – Noitidart Aug 16 '15 at 06:41
  • @Noitidart did you test this code http://pastebin.com/cBYuvW6D on scratchpad . – Madhawa Priyashantha Aug 16 '15 at 06:47
  • and here's my experiementation, please experiement with it too: https://gist.github.com/Noitidart/c6af5d6838a2e22c08ce yes i tested your code, i know the issue, and am trying to find an answer for it. the issue is SetForegroundWindow only works if the calling application has focus. – Noitidart Aug 16 '15 at 06:47
  • @Noitidart that code doesn't flsash icon on taskbar – Madhawa Priyashantha Aug 16 '15 at 06:50
  • Some variations of that code do, but our end goal is not to flash in the taskbar, we want to have the window focused. – Noitidart Aug 16 '15 at 06:52
  • Hey can you jump on the irc channel again I missed you, my username is noida I have to chat with you about this, if we launch an exe using `ShelExecuteEx` (im not a fan of `nsIProcess`) and focus use `SetForegroundWindow` from there it works great because on launch the launched app takes focus. The other option is to set window always on top, then `GetWindowRect` then `SetCursorPos` at 0,0 of that window and send a click. The people replying in that SO topic aren't offering a solution because they say it's against the systems terms, even though we can accomplish this with different ways. – Noitidart Aug 16 '15 at 17:45

1 Answers1

3

These solutions use js-ctypes winapi. This is a Windows solution. OSX and Linux don't have this issue, it is easy for steal focus there.

It would be easy if we could just do SetForegroundWindow winapi method, however the process calling this method must have user focus. If it does not, then it does nothing, so here are the ways to force it:

Attach to thread method (recommended method)

This method does the following:

  1. Gets the window currently in the foreground
  2. If none found, the the regular SetForegroundWindow will work so it does that and quits
  3. If window in foreground found it then gets the id of its thread
  4. It then compares that to the thread id of the current firefox (which is the target windows thread)
  5. If it is same then it uses regular SetForegroundWindow and quits
  6. If it is not same then it uses AttachThreadInput to attach the firefox's thread to the thread of the currently in foreground window
  7. If call in step 6 fails, then it gives up and quits the function, it throws an error
  8. If call in step 6 doesn't fail then it calls SetForegroundWindow as it WILL work now
  9. It then undoes the attachment done in step 6, so if you do a SetForegroundWindow call now it will fail as it normally should

I can't think of a reason AttachThreadInput will fail, but if it does, that is the only non-foolproof part of this method. If anyone knows why that will fail then please share a solution for that failure reason. However I left the SetCursorPos method below as an emergency fallback.

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);

Steal then restore cursor position method (not recommended method - is not fool proof, and since it simulates click - it may click on something like a link if user has window in fullscreen and the link was at position 0,0 of the window)

This is a solution that uses js-ctypes and works only on Windows operating system, that does the following:

  1. Sets the target window to be "always on top" (but focus is not in the window yet)
  2. Gets the current cursor position
  3. Sets the cursor position to the top left of the window
  4. Clicks to give it focus
  5. Restores the cursor position back to what it was
  6. Sets the window back to normal (not "always on top")

It has the issue where the x and y coords of the syntehsized mouse click in SendInput are not being respected, that's why I had to use SetCursorPos, in a future revision I want to fix this up so it doesn't use SetCursorPos as moving the users mouse is annoying to the user, but the benefit far outweights the slight annoyance, because its very important to give the window focus, I am temporarily accepting this SetCursorPos method because I do a "restore cursor position to before stealing", so here it is:

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);

This is the approach that was needed, because SetForegroundWindow does nothing if the process calling it is not the currently focused one.

Tested and this works even if another window is set to be "always on top", but because the other window is always on top, that will be the topmost after focus this window.

Noitidart
  • 35,443
  • 37
  • 154
  • 323
  • thanks for your detailed answer ..it's working very nicely – Madhawa Priyashantha Aug 17 '15 at 01:57
  • @FastSnail be sure to use the first method :) I learned a ton here too so thanks for it :) – Noitidart Aug 17 '15 at 02:28
  • Hey @FastSnail I noticed you're java/android guy, I was struggling with a very small script (im new to java, and actually convert it to JNI via firefox jsctypes) but could you please help me write a snippet that creates a transparent image and overlays it so it shows over everything, like skype does, this is android 4.0.4: ![](http://i.imgur.com/HIxj8VQ.png) see at top right, and also that ball in middle with `#`. Basically i wand to do same then screenshot it: http://stackoverflow.com/questions/31906925/screenshot-is-not-of-full-device-ui-but-only-of-view#comment51748038_31906925 – Noitidart Aug 17 '15 at 05:42
  • Also @FastSnail if you could write out how to do the screenshot with the Android 5.0 API as CommonsWare mentioned in that topic i linked and answer that question that would be so cool! Im on moz irc always if you ever can chat! :) – Noitidart Aug 17 '15 at 05:43
  • hello i have a very valuable restart extension(dictionary) .it is xul extension.it's not working now so i want to find the errors.unfortunatly there is no debug button for it .so how can i run it in cmd like i do for restartless addons `jpm run` ?? thanks you so much – Madhawa Priyashantha Jan 05 '16 at 13:05
  • 1
    @FastSnail you have to enable developer preferences - https://developer.mozilla.org/en-US/Add-ons/Setting_up_extension_development_environment#Development_preferences - and then use browser console via Ctrl + Shift + J – Noitidart Jan 05 '16 at 20:47
  • thanks you so much i create a new profile then set those preferences then i installed the not working extension.so now how can i see the errors off this extension .console show lot of errors .some are from the webpage i visit.so how can i see the error of that extension ?thanks – Madhawa Priyashantha Jan 06 '16 at 11:41
  • 1
    @FastSnail the browser console shows everything. I heard there is a way to debug only addon, I havent tried it yet, but try going to about:debugging then clicking "Debug" next to your addon. – Noitidart Jan 06 '16 at 21:38
  • thanks i was able to see error even there are lot of logs.but about:debugging says invalid url it seems like about:debugging doesn't exist ? – Madhawa Priyashantha Jan 07 '16 at 02:20
  • 1
    @FastSnail it exists for me - http://i.imgur.com/tOHnPen.png - I am using beta channel though, so it might be Firefox 44+. But the debugging specific addon im pretty sure was there before, maybe go to addn manager and clck debug - http://i.imgur.com/uQd7zJs.png - ? – Noitidart Jan 07 '16 at 04:06
  • i downgrade fx to 33 cuz i thought the extension has problem with newer versions .i will update to newer one.thanks – Madhawa Priyashantha Jan 07 '16 at 04:29