Sharing JavaScript Code in Firefox and IE Add-ons

Recently I ported a Firefox extension to Internet Explorer. The Firefox version has some rather complex application logic implemented in JavaScript, so I wanted to share the code in the IE version rather than reimplement it. I thought the others might be interested in the approach that I used.

technology-1283624_1920-1

My IE add-on is implemented in C++ as a Browser Helper Object, so first of all I needed to find a way to call into JavaScript code. I ended up using a Windows Script Component, which is a COM component implemented in a scripting language. Normally this will be VBScript or JScript, which is Microsoft's implementation of ECMAScript, the standardized version of JavaScript. (After all, why have one name for something when you can have three?) The component gets a UUID and can be instantiated and invoked like any other COM object. In my case, the Script Component (let's call it foo.wsc) looks something like this:

<?xml version="1.0"?>
<component>
<registration
 description="Foo"
 progid="Foo.WSC"
 version="1.00"
 classid="{4d137343-21de-4f5d-9a9e-0bd3bd8e1c9a}"
>
</registration>
<public>
 <method name="myMethod">
 <parameter name="param1" />
 <parameter name="param2" />
 </method>
</public>
<script src="sharedjs/foo.js" />
<script language="JScript">
<![CDATA[
function myMethod(param1, param2)
{
  var foo = new Foo;
  foo.mySharedMethod(param1, param2);
}
]]>
</script>
</component>

The trick is to inject in the actual shared code (foo.js in this case) using a relative path from wherever the .wsc file resides. We can use the same code from the Firefox extension by using the Text Preprocessor. (Note that to use this approach, you have to build your extension using the Mozilla build system as explained in this fascinating, brilliantly well-written article.) Let's say we have an XPCOM component that needs to implement the same functionality. The actual component file becomes a sort of wrapper around the shared JS code:

#filter substitution

const Cc = Components.classes;
const Ci = Components.interfaces;

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

#include ../sharedjs/foo.js

Foo.prototype.classDescription = "Foo!";
Foo.prototype.classID = Components.ID("{37025f65-7e5b-4eac-8aa7-bc85b7a190d5}");
Foo.prototype.contractID = "@salsitasoft.com/foo;1";
Foo.prototype.QueryInterface = XPCOMUtils.generateQI(
 [
   Ci.nsIClassInfo,
   Ci.nsIFoo
 ]
);

// nsIClassInfo
Foo.prototype.implementationLanguage = Ci.nsIProgrammingLanguage.JAVASCRIPT;
Foo.prototype.flags = Ci.nsIClassInfo.DOM_OBJECT;
Foo.prototype.getInterfaces = function getInterfaces(aCount) {
 var interfaces = [
   Ci.nsIClassInfo,
   Ci.nsIFoo
 ];
 aCount.value = interfaces.length;
 return interfaces;
}
Foo.prototype.getHelperForLanguage = function getHelperForLanguage(aLanguage) {
 return null;
}

function NSGetModule(compMgr, fileSpec) {
 return XPCOMUtils.generateModule([Foo]);
}

Notice that there is no implementation for mySharedMethod in the component itself since it is implemented directly on the Foo object in the shared JavaScript file. The same approach can be used to access the shared code from a JavaScript module or even a chrome file. In my case, I had some code that couldn't be shared easily between Firefox and IE, so I factored that out into two files (let's call them ie.js and ff.js). The .wsc includes the former (using a <script> tag) and the Firefox components and modules include the latter (using the #include directive). So if I want to e.g. instantiate an XMLHTTPRequest using XPCOM in Firefox and ActiveX in IE, I can implement a method called newXMLHttpRequest with the appropriate implementation in each of the browser-specific script files and then call it from the shared JavaScript code.

ABOUT SALSITA

Salsita is a digital product agency that designs and develops exceptional web and mobile solutions which differentiate our clients, engage their customers, and grow their business. We provide design-driven development services that cover the entire software development cycle.

Our areas of specialization include eCommerce, web and mobile dashboards, online content editors and data visualization. We leverage the Salsita 3D Product Configurator Framework to develop slick, cost-effective 3D product configurator, visualization, and design solutions for brands and retailers across industries from consumer electronics to furniture, fashion and more.

We have developed award-winning digital solutions for dozens of companies, ranging from household names like eBay, SAP, Texas Instruments, and First American Financial, to innovative startups across Europe and North America.

Contact us