"use strict";
const EXPORTED_SYMBOLS = ["Corrector"];
const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/ctypes.jsm");
const MAPVK_VK_TO_VSC = 0;
const Corrector = {
init: function Corrector_init(api) {
this.englishLayout = (new this.HKL())(67699721);
this._loadlibraries();
}
,
finalize: function Corrector_finalize() {
this._libraries.user32.close();
this._libraries.kernel32.close();
}
,
_getSystem32LibPath: function Corrector__getSystem32LibPath(dllName) {
var sysPath = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("SysD",Ci.nsIFile);
sysPath.append(dllName);
return sysPath.path;
}
,
HKL: function Corrector_HKL() {
return ctypes.StructType("HKL",[{
"unused": ctypes.int}]);
}
,
GetCurrentThreadId: function Corrector_GetCurrentThreadId() {
return this._libraries.kernel32.declare("GetCurrentThreadId",ctypes.winapi_abi,ctypes.int32_t);
}
,
GetKeyboardLayout: function Corrector_GetKeyboardLayout(struct) {
return this._libraries.user32.declare("GetKeyboardLayout",ctypes.winapi_abi,ctypes.int,struct);
}
,
GetKeyboardLayoutList: function Corrector_GetKeyboardLayoutList() {
return this._libraries.user32.declare("GetKeyboardLayoutList",ctypes.winapi_abi,ctypes.int32_t,ctypes.int32_t,ctypes.voidptr_t);
}
,
VkKeyScanExW: function Corrector_VkKeyScanExW(struct) {
return this._libraries.user32.declare("VkKeyScanExW",ctypes.winapi_abi,ctypes.int16_t,ctypes.jschar,struct);
}
,
MapVirtualKeyW: function Corrector_MapVirtualKeyW(struct) {
return this._libraries.user32.declare("MapVirtualKeyW",ctypes.winapi_abi,ctypes.int32_t,ctypes.int,ctypes.int);
}
,
ToUnicodeEx: function Corrector_ToUnicodeEx(struct) {
return this._libraries.user32.declare("ToUnicodeEx",ctypes.winapi_abi,ctypes.int,ctypes.int,ctypes.int,ctypes.char.ptr.array(),ctypes.jschar.ptr,ctypes.int,ctypes.int,struct);
}
,
_loadlibraries: function Corrector__loadlibraries() {
this._libraries = {
};
this._libraries.user32 = ctypes.open(this._getSystem32LibPath("User32.dll"));
if (this._libraries.user32 == null)
throw new Error("Unable to load User32.dll library");
this._libraries.kernel32 = ctypes.open(this._getSystem32LibPath("Kernel32.dll"));
if (this._libraries.kernel32 == null)
throw new Error("Unable to load User32.dll library");
}
,
_getKeyboardLayoutList: function Corrector__getKeyboardLayoutList() {
var layoutsCount = this.GetKeyboardLayoutList()(0,null);
var array = new ctypes.ArrayType(this.HKL(), layoutsCount);
var theLayouts = new array();
this.GetKeyboardLayoutList()(layoutsCount,theLayouts.address());
return theLayouts;
}
,
_determineCharLayout: function Corrector__determineCharLayout(char, systemLocales) {
for (let i = 0;i < systemLocales.length;i++) {
let hkl = new this.HKL();
let virtualKey = this.VkKeyScanExW(hkl)(char,hkl(systemLocales[i].unused));
if (virtualKey != - 1)
return systemLocales[i];
}

return null;
}
,
_determineTextLayout: function Corrector__determineTextLayout(aString, systemLocales) {
var textLayout;
var i = 0;
while (i < aString.length) {
textLayout = this._determineCharLayout(aString[i++],systemLocales);
if (textLayout)
break;
}

if (! textLayout && i == aString.length)
return null;
var layouts = {
text: textLayout,
alt: null};
if (textLayout.unused != this.englishLayout.unused)
layouts.alt = this.englishLayout; else
if (systemLocales.length > 1)
layouts.alt = textLayout.unused == systemLocales[0].unused ? systemLocales[1] : systemLocales[0];
return layouts;
}
,
_translateChar: function Corrector__translateChar(ch, srcLayout, dstLayout) {
if (/[\d]/.test(ch))
return null;
var hkl = new this.HKL();
var virtualKey = this.VkKeyScanExW(hkl)(ch,hkl(srcLayout.unused));
if (virtualKey == - 1)
return null;
var scanCode = this.MapVirtualKeyW()(virtualKey,MAPVK_VK_TO_VSC);
var hkl2 = new this.HKL();
var buf = new new ctypes.ArrayType(ctypes.jschar, 1)();
var translated = this.ToUnicodeEx(hkl2)(virtualKey,scanCode,ctypes.char.ptr.array(255)(),buf,1,0,hkl2(dstLayout.unused));
if (translated == 1)
{
let result = buf.readString();
if (! /[\d]/.test(result))
return result;
}

return null;
}
,
_translateString: function Corrector__translateString(aString, srcLayout, dstLayout) {
var resultStr = "";
for each(let ch in aString) {
let upper = ch.toLowerCase() != ch;
let ret = this._translateChar(ch,srcLayout,dstLayout);
if (ret != null)
resultStr += upper ? ret.toUpperCase() : ret; else
resultStr += ret;
}

return resultStr;
}
,
getSwitchedLayout: function Corrector_getSwitchedLayout(aString) {
if (! aString)
return aString;
var systemLocales = this._getKeyboardLayoutList();
var layouts = this._determineTextLayout(aString,systemLocales);
if (layouts && layouts.alt != null && layouts.alt.unused != 0)
return this._translateString(aString,layouts.text,layouts.alt);
return aString;
}
,
hasCurrentLayout: function Corrector_hasCurrentLayout(text) {
var threadID = this.GetCurrentThreadId()();
var hkl = new this.HKL();
var currentLayout = this.GetKeyboardLayout(hkl)(hkl(threadID));
var systemLocales = this._getKeyboardLayoutList();
var layouts = this._determineTextLayout(text,systemLocales);
return ! ! (layouts && layouts.text.unused == currentLayout);
}
};
