Firefox Hacks [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Firefox Hacks [Electronic resources] - نسخه متنی

Nigel McFarlane

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید






Hack 82. Add a New XPCOM Component

Extend Firefox's component
library with a new object that's usable in the
chrome.

Firefox comes preinstalled with over a thousand XPCOM objects (classes and
interfaces). Most of these objects provide fundamental services, such
as access to files, networks, and data. Nearly all of these objects
are written in C++ and are provided in compiled libraries. This hack
shows how to add new components written in JavaScript. Typically, new
objects provide application-level abstractions on top of the existing
libraries, but in theory, they can do anything.

We'll use as an example the creation of a simple
persistence classone that dumps a simple string of information
to a disk-based file. We'll put all the required
logic into a single script named
nsStringSave.js, which we'll
put in the components directory in the install
area. We'll use limited error checking, which could
easily be done more robustly.

The whole component creation process comes down to dancing to the
beat of the XPIDL (Cross Platform
Interface Definition Language) drum. Mozilla interfaces are specified
in XPIDL. They're part of the Mozilla source code;
you can also download them in convenient form from http://www.nigelmcfarlane.com/books/radmoz_supportl.


7.9.1. Naming the New Component


Components and interfaces must be defined by both names and numbers.
We're creating one new component and one new
interface, so that's two names and two numbers. To
get the name, we make it up, using some existing conventions. To get
the number (called an ID), we run uuidgen or
guidgen from the command line or /msg
mozbot uuid
on the #mozilla channel of
[scriptable, uuid(A35ED730-4817-4BE3-943E-75E9124FC4D7)]
interface nsIStringSave : nsISupports
{
void set_file(in string pathname);
void dump(in string str);
};

All components should implement the nsISupports
interface, which is the fundamental interface. We generate a type
library from this file. The original IDL serves as documentation
only. To make this library, you need the tool
xpidl. It's available in nightly
Mozilla builds
[Hack #92] . Use it
to generate a nsIStringSave.xpt file. On
UNIX/Linux/Macintosh:

xpidl -m typelib nsIStringSave.idl

On Windows:

Xpidl.exe -m typelib nsIStringSave.idl

Put this .xpt file in the
components directory.

Component names are defined along with the componentin this
case, done as follows in JavaScript:

const SSComponentName = "@mozilla.org/example/string-save;1";
const SSClassID = Components.ID('{a81d9b27-6adc-47c7-a780-dced1e1e4cb3}');

Again, uuidgen is used to create a unique
identifier. We'll put these statements in a script
shortly. Naming of the new component is now complete.


7.9.2. Creating and Implementing a Module


Modules are the ultimate containers for XPCOM components. Each module
holds zero or more components. Modules themselves implement the
nsIModule interface. Here's our
new module in JavaScript:

const SSComponentName = "@mozilla.org/example/string-save;1";
const SSClassID = Components.ID('{a81d9b27-6adc-47c7-a780-dced1e1e4cb3}');
var nsStringSaveModule = {
getClassObject : function (mgr, cid, iid, ) {
if ( !cid.equals(SSClassID) ) throw "unknown";
var component = (new nsStringSave( )).QueryInterface(iid);
component._init( ); // the module knows its components
return component;
},
registerSelf : function ( ) { /* contstructor - does nothing here */ },
unregisterSelf : function ( ) { /* destructor - does nothing here */},
canUnload : function ( ) { return false; /* can't unload */ }
};

The getClassObject() method checks its arguments and returns
an instance of the only component that it knows about. The module
object does nothing by itself; an equivalent of C's
main() is required. For module definition
scripts, Firefox expects to find the special hook function named
NSGetModule(), so we add that as well:

function NSGetModule(  ) { return nsStringSaveModule; }


7.9.3. Creating and Implementing a Component


The getClassObject() method in the previous
section creates an object that's an instance of the
component. It uses the following custom JavaScript object constructor
to do so. Append this lot to the file containing the module code:

var Cc = Components.classes;     // shorthand
var Ci = Components.interfaces; // shorthand
function nsStringSave ( ) {};
nsStringSave.prototype = {
// --- "private" data ---
_file : null,
_stream : null,
_init : function ( ) {
_file = Cc["@mozilla.org/local/file;1"].createInstance(Ci.nsILocalFile);
_stream = Cc["@mozilla.org/network/file-output-stream;1"];
_stream = _stream.createInstance(Ci.nsIFileOutputStream);
},
// --- nsIStringSave ---
set_file : function (path) {
_file.initWithPath(path);
_stream.close( );
_stream.init(_file, 0x0A, 0644, true);
},
dump : function (str) {
_stream.write(str, str.length);
},
// --- nsISupports ---
QueryInterface : function (iid) {
if ( !iid.equals(Components.interfaces.nsISupports) ) throw "unknown";
if ( !iid.equals(Components.interfaces.nsIStringSave) ) throw "unknown";
return this;
}
};

There's a bit of complexity at work here, both in
the use of JavaScript prototypes and in the opening and administering
of file objects. The important bit is that the object constructor
constructs an object that implements the two required interfaces:
nsISupports and nsIStringSave.


7.9.4. Installing the Component


The nsIStringSave.js file, once complete, should
be put in the components directory. After that,
we have to fool Firefox into thinking that the install
area's been patched with new files (in truth, it
has). Once that misdirection is achieved, Firefox will do extra work
the next time it starts up. It will do a big audit of all the stuff
it has to work with, and that includes discovering all XPCOM modules
and components.


Make a backup of the user profile's
compreg.dat before proceeding.

To flag that the audit should occur, move to the user profile area.
Find the file named compatibility.ini. Edit it
with a text editor and decrement the Build ID
date by one day. Save the file in place.

When Firefox next starts up, it will do its big audit because it
thinks that it has been patched (the build date has gone up). It will
find the nsStringSave.xpt file and also the new
NSGetModule() call. From those things and others, it
will write out a file called compreg.dat. Each
time Firefox starts up subsequently, it will rely on the
compreg.dat file for an accurate list of
available objects. When getting ready for the audit, it is therefore
critical that you back up this file, in case there are programming
errors in your new modules, components, or interfaces. Those errors
will cause the regenerated compreg.dat file to
be flawed, and in that case, you'll need the old one
to go back to.


7.9.5. Running the Component


From any secure scripting environment (in the chrome, a signed web
page, or using the
xpcshell testing tool) this is all the code you
need to use your new component:

var saver = Components.classes["@mozilla.org/example/string-saver;1"];
saver = saver.createInstance(Components.interfaces.nsIStringSaver);
saver.set_file("C:\tmp\test.dat");
saver.dump("A test string of no particular import");

xpcshell is a small, non-GUI JavaScript
interpreter available in nightly builds that can manipulate XPCOM
objects.


/ 164