Firefox bootstrapped extension: Get native HWND handle of the browser window - firefox-addon

I have an external application and I want it to display some information on top of the browser window. My bootstrapped extension needs to pass the browser window handle (native HWND) to my application, along with some other useful information about the window. I'm able to do the communication between them, the only thing that is missing is a way to get the native HWND of the Firefox window.
I read a lot about it and although I belive it's possible, I couldn't find a working solution. Here's what I've tried so far:
This one should give me nsIBaseWindow, so I could get nsIBaseWindow.nativeHandle or nsIBaseWindow.ParentNativeWindow, but no success:
var window = SomeDOMWindow; // Informative
var baseWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
.treeOwner
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIXULWindow)
.docShell
.QueryInterface(Components.interfaces.nsIBaseWindow);
The above code is widely spread on forums, but I couldn't get it to work for me.
The other one does not seem to be much accurate since it gets the HWND based on the window's class and title, which can lead to wrong results:
Components.utils.import("resource://gre/modules/ctypes.jsm");
var lib = ctypes.open("user32.dll");
var fww = lib.declare("FindWindowW", ctypes.winapi_abi,
ctypes.voidptr_t, ctypes.jschar.ptr, ctypes.jschar.ptr);
var sfw = lib.declare("SetForegroundWindow", ctypes.winapi_abi,
ctypes.int32_t, ctypes.voidptr_t);
var hwnd = fww("MozillaWindowClass", document.title);
setTimeout(function() {
sfw(hwnd);
lib.close();
}, 3000);
Any help would be appreciated.

window must be a root one (i.e. an instance of ChromeWindow)
The following code should work
var win = Cc["#mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator).getMostRecentWindow("navigator:browser");
var basewindow = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.nsIBaseWindow;
var nativehandle = basewindow.nativeHandle;

The problem was that I was querying the wrong interface from the subject param in the xul-window-registered observer. I need to get an nsIDOMWindow instead of an nsIXULWindow so the first code mentioned in my question works. So now I'm doing the following, with some piece of code #Noit suggested:
observe: function(subject, topic, data) {
var newWindow = subject.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
var basewindow = newWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.nsIBaseWindow;
var nativehandle = basewindow.nativeHandle;
}
And it works!
Thank you very much for your help.

I also just came across this, it might be nice:
Cu.import("resource://gre/modules/ctypes.jsm");
/*start getcursorpos*/
var lib = ctypes.open("user32.dll");
/*foreground window stuff*/
var FindWindowA = lib.declare('FindWindowA', ctypes.winapi_abi, ctypes.uint32_t, ctypes.jschar.ptr, ctypes.jschar.ptr)
var GetForegroundWindow = lib.declare('GetForegroundWindow', ctypes.winapi_abi, ctypes.uint32_t)
function doFindWindow() {
var wm = Cc['#mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator);
var title = wm.getMostRecentWindow('navigator:browser').gBrowser.contentDocument.title;
Cu.reportError('title=' + title)
var ret = FindWindowA('', title + ' - Mozilla Firefox');
//var ret = GetForegroundWindow();
Cu.reportError(ret);
}
/*end foreground window stuff*/

The code in the answer of user 'paa' worked until Firefox version 69.
If you execute it in Firefox 70 you will get an exception:
TypeError: win.QueryInterface is not a function
This is strange because the variable win has the same content in Firefox 69 and 70.
When I execute alert(win) I get: "[object ChromeWindow]" in both browsers.
And alert(win.document.title) displays correctly the title of the document in both browsers.
I downloaded the sourcecode of both Firefox versions to compare them and possibly find the cause. But the source code of Firefox is huge (2 Gigabyte) and nearly completely free of comments. I found that I'm wasting my time with that approach.
It is extremely difficult to understand sourcecode of Firefox which runs spread over multiple processes which communicate with each other. It seems that the content of the variable win corresponds to the C++ class mozIDOMWindowProxy or nsChromeOuterWindowProxy. But these seem to be only wrapper classes for other classes. Finally I gave up trying to understand Firefox sourcecode.
But playing around for some hours I finally found a solution by try and error.
It is even simpler:
var baseWindow = win.docShell
.treeOwner
.nsIBaseWindow;
It works on Firefox 70 up to 79 (which is currently the latest version). However this new code does not run on Firefox versions <= 62. On Firefox 62 or older you get the error
TypeError: win.docShell is undefined
So the Firefoxes from 63 to 69 allow both versions of code. Maybe in version 70 the QueryInterface() has been removed because it is not needed anymore and has become legacy?
NOTE: In Firefox 68 they made another change. Now there are 2 native windows: The toplevel 'MozillaWindowClass' now has a child window 'MozillaCompositorWindowClass' which runs in another process and draws the web content.

Related

Differentiating Mode 2 Form 1 from Mode 2 Form 2 on XA CD-ROMs?

I'm developing a library for reading CD-ROMs and the ISO9660 file system.
Long story short, pretty much everything is working except for one thing I'm having a hard time figuring out how it's done:
Where does XA standard defines differentiation among Mode 2 Form 1 from Mode 2 Form 2?
Currently, I am using the following pseudo-code to differentiate between both forms; albeit it's a naive heuristic, it does work but well, it's far from ideal:
var buffer = ... // this is a raw sector of 2352 bytes
var m2F1 = ISector.Cast<SectorMode2Form1>(buffer);
var edc1 = EdcHelper.ComputeBlock(0, buffer, 16, 2056);
var edc2 = BitConverter.ToUInt32(m2F1.Edc, 0);
var isM2F1 = edc1 == edc2;
if (isM2F1) return CdRomSectorMode.Mode2Form1;
// NOTE we cannot reliably check EDC of M2F2 since it's optional
var isForm2 =
m2F1.SubHeaderCopy1.SubMode.HasFlag(SectorMode2Form1SubHeaderSubMode.Form2) &&
m2F1.SubHeaderCopy2.SubMode.HasFlag(SectorMode2Form1SubHeaderSubMode.Form2);
if (isForm2) return CdRomSectorMode.Mode2Form2;
return CdRomSectorMode.Mode2Formless;
If you look at some software like IsoBuster, it appears to be a track-level property, however, I'm failing to understand where the value would be read from within the track.
I'm actually doing something similar in typescript for my ps1 mod tools. It seems like you actually probably have it correct here, since I'm going to assume your HasFlag check is checking position bit position 6 of the subheader. If that flag is set, you are in form 2.
So what you probably want something like:
const sectorBytes = new Uint8Arrray(buffer);
if (sectorBytes[0x012] & 0x20) === 0x20) {
return CdRomSectorMode.Mode2Form2;
} else {
return CdRomSectorMode.Mode2Form1;
}
You could of course use the flag code you already have, but that would require you to use one of the types first to get that. This just keeps it generic bytes and checks the flag, then returns the relevant mode.

Block script execution in Firefox extension?

Probably looking for an answer to an age-old question, but I would like to block script execution. In my use-case blocking the browser is acceptable.
Also, in my use-case I am trying to do this from a Firefox extension, which means my code is "Chrome code", running in the browser environment.
This can easily be done by using a modal window, then programmatically closing the window. So this demonstrates that there is a blocking mechanism that exists.
Is there any way to achieve modal blocking without actually creating or opening the modal window? Some way to tap into the blocking mechanism used for modal windows?
I've done a lot of searching on this subject, but to no avail.
Using nsIProcess you can block the thread.
You can create an executable which has a sleep or usleep method or equivalent. Then run the process synchronously (nsIProcess.run) and set blocking argument to true.
Of course for portability you will need to create an executable appropriate for each platform you wish to support, and supply code for discrimination.
Basic code is something like the following. I have verified on 'nix (Mac OS X) this code to work, using a bash script with only the line sleep .03:
let testex = Components.classes["#mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsIFile);
testex.initWithPath("/Users/allasso/Desktop/pause.sh");
let process = Components.classes["#mozilla.org/process/util;1"]
.createInstance(Components.interfaces.nsIProcess);
process.init(testex);
let delay = 30; // convert this to milliseconds in the executable
process.run(true,[delay],1); // `run` method runs synchronously, first arg says to block thread
In an extension you probably would want to make your nsIFile file object more portable:
Components.utils.import("resource://gre/modules/FileUtils.jsm");
let testex = FileUtils.getFile("ProfD",["extension#moz.org","resources","pause.sh"]);
Of course keep in mind that Javascript is basically single-threaded, so unless you are blocking a thread spawned using Web Workers you will be freezing the entire UI during the sleep period (just like you would if you opened a modal window).
References:
https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIProcess
https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFile
https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O#Getting_special_files
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/basic_usage
OPTION 1
There is enterModalState and leaveModalState in nsIDOMWindowUtils here: MDN :: nsIDOMWindowUtils Reference
However they don't seem to work for me. This topic might explain why: nsIDOMWindowUtils.isInModalState() not working they topic says isInModalState is marked [noscript] which I see, but enterModalState and leaveModalState are not marked [noscript] I have no idea why it's not working.
What does work for me though is suppressEventHandling:
var utils = Services.wm.getMostRecentWindow('navigator:browser').
QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
utils.suppressEventHandling(true); //set arg to false to unsupress
OPTION 2
You can open a tiny window with the source window as the window you want to make modal and as dialog but open it off screen. Its dialog so it wont show a new window the OS tab bars. However hitting alt+f4 will close that win, but you can attach event listeners (or maybe use the utils.suppressEventHandling so keyboard doesnt work in it) to avoid the closing till you want it closed. Here's the code:
var sDOMWin = Services.wm.getMostRecentWindow(null);
var sa = Cc["#mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
var wuri = Cc["#mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
wuri.data = 'about:blank';
sa.AppendElement(wuri);
let features = "chrome,modal,width=1,height=1,left=-100";
if (PrivateBrowsingUtils.permanentPrivateBrowsing || PrivateBrowsingUtils.isWindowPrivate(sDOMWin)) {
features += ",private";
} else {
features += ",non-private";
}
var XULWindow = Services.ww.openWindow(sDOMWin, 'chrome://browser/content/browser.xul', null, features, sa);
/*
XULWindow.addEventListener('load', function() {
var DOMWindow = XULWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
DOMWindow.gBrowser.selectedTab.linkedBrowser.webNavigation.stop(Ci.nsIWebNavigation.STOP_ALL);
DOMWindow.gBrowser.swapBrowsersAndCloseOther(DOMWindow.gBrowser.selectedTab, aTab);
//DOMWindow.gBrowser.selectedTab = newTab;
}, false);
*/

iframe.contentDocument.documentElement.scrollHeight - Dart equivalent

final html.IFrameElement iframe = rootDemoElement.querySelector("iframe");
final int contentHeight = <???>.scrollHeight;
this works in JS:
var contentHeight = iframe.contentDocument.documentElement.scrollHeight;
contentDocument is not available in Dart.
Is it really possible that contentDocument is missing in Dart?
Here is my solution:
var jsIFrame = new JsObject.fromBrowserObject(iframe);
var contentHeight = jsIFrame["contentDocument"]["documentElement"]["scrollHeight"];
As far as I know there was an attempt to make Dart in the browser more secure than JavaScript and this led to a model where cross-window communication was limited (to postMessage). I assume an Iframe suffers from the same limitations. There was a comment on an issue that they want to leave this strategy because this is usually circumnavigated by using dart-js-interop anyway.
I think the main culprit is that you get a _DOMWindowCrossFrame instead of a Window instance.
See
http://dartbug.com/17936#c2
probably also related
http://dartbub.com/20146
http://dartbug.com/20143
http://dartbug.com/20173
http://dartbug.com/21219
http://dartbug.com/20216
http://dartbug.com/19610
http://dartbug.com/16814
http://dartbug.com/12788
http://dartbug.com/2312

How to implement IndexDB in IOS

I am developing a mobile application using phonegap, Initially I have developed using WEBSQL but now I m planning to move it on INDEXDB. The problem is it does not have direct support on IOS , so on doing much R&D I came to know using IndexedDB Polyfil we can implement it on IOS too
http://blog.nparashuram.com/2012/10/indexeddb-example-on-cordova-phonegap.html
http://nparashuram.com/IndexedDBShim/
Can some please help me how to implement this as there are not enough documentation for this and I cannot figure out a any other solution / api except this
I have tested this on safari 5.1.7
Below is my code and Error Image
var request1 = indexedDB.open(dbName, 5);
request1.onsuccess = function (evt) {
db = request1.result;
var transaction = db.transaction(["AcceptedOrders"], "readwrite");
var objectStore = transaction.objectStore("AcceptedOrders");
for (var i in data) {
var request = objectStore.add(data[i]);
request.onsuccess = function (event) {
// alert("am again inserted")
// event.target.result == customerData[i].ssn;
};
}
};
request1.onerror = function (evt) {
alert("IndexedDB error: " + evt.target.errorCode);
};
Error Image
One blind guess
Maybe your dbName contains illegal characters for WebSQL database names. The polyfill doesn't translate your database names in any kind. So if you create a database called my-test, it would try to create a WebSQL database with the name my-test. This name is acceptable for an IndexedDB database, but in WebSQL you'll get in trouble because of the - character. So your database name has to match both, the IndexedDB and the WebSQL name conventions.
... otherwise use the debugger
You could set a break point onto your alert(...); line and use the debugger to look inside the evt object. This way you may get either more information about the error itself or more information to share with us.
To do so, enable the development menu in the Safari advanced settings, hit F10 and go to Developer > Start debugging JavaScript (something like that, my Safari is in a different language). Now open then "Scripts" tab in the developer window, select your script and set the break point by clicking on the line number. Reload the page and it should stop right in your error callback, where you can inspect the evt object.
If this doesn't help, you could get the non-minified version of the polyfill and try set some breakpoints around their open function to find the origin of this error.
You could try my open source library https://bitbucket.org/ytkyaw/ydn-db/wiki/Home. It works on iOS and Android.

Why Won't this Clipboard Code Pass Mozilla Validation?

Salve! When I try Mozilla's Validator on my addon, it get the following error related to my treatment of clipboard usage:
nsITransferable has been changed in Gecko 16.
Warning: The nsITransferable interface has changed to better support
Private Browsing Mode. After instantiating the object, you should call
the init function on it before any other functions are called.
See https://developer.mozilla.org/en-US/docs/Using_the_Clipboard for more
information.
var trans = Components.classes["#mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
if ('init' in trans){ trans.init(null);};
I can't understand this.
Here is my code - I am clearly calling trans.init:
var clip = Components.classes["#mozilla.org/widget/clipboard;1"].getService(Components.interfaces.nsIClipboard);
if (!clip) return "";
var trans = Components.classes["#mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
if ('init' in trans){ trans.init(null);}; //<--IT DOESN'T LIKE THIS
if (!trans) return false;
trans.addDataFlavor("text/unicode");
I've also tried the Transferable function from Mozilla's example here, but get the same non-validation report.
One of the Mozilla AMO editors told me to write exactly this, and it still doesn't validate.
I've also tried, simply:
var trans = Components.classes["#mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
trans.init(null); //<---LOOK HERE
if (!trans) return false;
trans.addDataFlavor("text/unicode");
The Validator does not report any errors - just this warning. Everything works properly. Mozilla updated their Gecko engine, and they want devlopers to match up to the new standard.
In my usage, we want to be able to use the contents of the clipboard that was probably gotten from outside the application, too, so we do want to call the init function with null instead of window.
Any advice would be wonderful!
trans.init(null) is valid in some circumstances, such as yours. It can also cause privacy leaks if used in the wrong circumstances, so the validator flags all uses of it as potentially requiring changing. Therefore, it is a warning that you can ignore in this case.

Resources