~minos-archive/minos/firefox-minos-settings

« back to all changes in this revision

Viewing changes to firefox27/firefox/h5xyzl6e.default/extensions/chmfox@zhuoqiang.me/components/chmfox.js

  • Committer: Javier Lopez
  • Date: 2015-07-08 01:10:19 UTC
  • Revision ID: git-v1:d8c3500bae00a8a67d74f8f96235ec11d75231be
rename mozilla-minos-settings -> firefox-minos-settings

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
2
Components.utils.import("resource://gre/modules/ctypes.jsm");
 
3
 
 
4
 
 
5
var EXPORTED_SYMBOLS = [ "Chmfox"];
 
6
 
 
7
if ("undefined" == typeof(Chmfox)) {
 
8
 
 
9
const Chmfox = (function() {
 
10
 
 
11
const Ci = Components.interfaces;
 
12
const Cc = Components.classes;
 
13
const Cr = Components.results;
 
14
 
 
15
var Application = Cc["@mozilla.org/fuel/application;1"].getService(Ci.fuelIApplication);
 
16
var xulRuntime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
 
17
 
 
18
const prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("extensions.chmfox.");
 
19
 
 
20
const kScheme = 'chm';
 
21
 
 
22
const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
 
23
 
 
24
function utf8Encode(string) {
 
25
    var utftext = "";
 
26
        for (var n = 0; n < string.length; n++) {
 
27
                var c = string.charCodeAt(n);
 
28
                if (c < 128) {
 
29
                        utftext += String.fromCharCode(c);
 
30
                }
 
31
                else if((c > 127) && (c < 2048)) {
 
32
                        utftext += String.fromCharCode((c >> 6) | 192);
 
33
                        utftext += String.fromCharCode((c & 63) | 128);
 
34
                }
 
35
                else {
 
36
                        utftext += String.fromCharCode((c >> 12) | 224);
 
37
                        utftext += String.fromCharCode(((c >> 6) & 63) | 128);
 
38
                        utftext += String.fromCharCode((c & 63) | 128);
 
39
                }
 
40
        }
 
41
        return utftext;
 
42
};
 
43
 
 
44
function log(message) {
 
45
    if (false) // Disable log for release
 
46
    {
 
47
        var console = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
 
48
        var msg = "[chmfox] " + message + "\n";
 
49
        console.logStringMessage(msg);
 
50
        dump(msg);
 
51
    }
 
52
}
 
53
 
 
54
function getCharsetFromLcid(lcid) {
 
55
    switch (lcid) {
 
56
    case 0x0436: return "ISO-8859-1"; // "Afrikaans", "Western Europe & US"
 
57
    case 0x041c: return "ISO-8859-2"; // "Albanian", "Central Europe"
 
58
    case 0x0401: return "ISO-8859-6"; // "Arabic_Saudi_Arabia", "Arabic"
 
59
    case 0x0801: return "ISO-8859-6"; // "Arabic_Iraq", "Arabic"
 
60
    case 0x0c01: return "ISO-8859-6"; // "Arabic_Egypt", "Arabic"
 
61
    case 0x1001: return "ISO-8859-6"; // "Arabic_Libya", "Arabic"
 
62
    case 0x1401: return "ISO-8859-6"; // "Arabic_Algeria", "Arabic"
 
63
    case 0x1801: return "ISO-8859-6"; // "Arabic_Morocco", "Arabic"
 
64
    case 0x1c01: return "ISO-8859-6"; // "Arabic_Tunisia", "Arabic"
 
65
    case 0x2001: return "ISO-8859-6"; // "Arabic_Oman", "Arabic"
 
66
    case 0x2401: return "ISO-8859-6"; // "Arabic_Yemen", "Arabic"
 
67
    case 0x2801: return "ISO-8859-6"; // "Arabic_Syria", "Arabic"
 
68
    case 0x2c01: return "ISO-8859-6"; // "Arabic_Jordan", "Arabic"
 
69
    case 0x3001: return "ISO-8859-6"; // "Arabic_Lebanon", "Arabic"
 
70
    case 0x3401: return "ISO-8859-6"; // "Arabic_Kuwait", "Arabic"
 
71
    case 0x3801: return "ISO-8859-6"; // "Arabic_UAE", "Arabic"
 
72
    case 0x3c01: return "ISO-8859-6"; // "Arabic_Bahrain", "Arabic"
 
73
    case 0x4001: return "ISO-8859-6"; // "Arabic_Qatar", "Arabic"
 
74
    case 0x042b: return null; //        "Armenian","Armenian"
 
75
    case 0x042c: return "ISO-8859-9"; // "Azeri_Latin", "Turkish"
 
76
    case 0x082c: return "cp1251"; //    "Azeri_Cyrillic", "Cyrillic"
 
77
    case 0x042d: return "ISO-8859-1"; // "Basque", "Western Europe & US"
 
78
    case 0x0423: return "cp1251"; //    "Belarusian", "Cyrillic"
 
79
    case 0x0402: return "cp1251"; //    "Bulgarian", "Cyrillic"
 
80
    case 0x0403: return "ISO-8859-1"; // "Catalan", "Western Europe & US"
 
81
    case 0x0404: return "BIG5"; //      "Chinese_Taiwan", "Traditional Chinese"
 
82
    case 0x0804: return "GBK"; //       "Chinese_PRC", "Simplified Chinese"
 
83
    case 0x0c04: return "BIG5"; //      "Chinese_Hong_Kong", "Traditional Chinese"
 
84
    case 0x1004: return "GBK"; //       "Chinese_Singapore", "Simplified Chinese"
 
85
    case 0x1404: return "BIG5"; //      "Chinese_Macau", "Traditional Chinese"
 
86
    case 0x041a: return "ISO-8859-2"; // "Croatian", "Central Europe"
 
87
    case 0x0405: return "ISO-8859-2"; // "Czech", "Central Europe"
 
88
    case 0x0406: return "ISO-8859-1"; // "Danish", "Western Europe & US"
 
89
    case 0x0413: return "ISO-8859-1"; // "Dutch_Standard", "Western Europe & US"
 
90
    case 0x0813: return "ISO-8859-1"; // "Dutch_Belgian", "Western Europe & US"
 
91
    case 0x0409: return "ISO-8859-1"; // "English_United_States", "Western Europe & US"
 
92
    case 0x0809: return "ISO-8859-1"; // "English_United_Kingdom", "Western Europe & US"
 
93
    case 0x0c09: return "ISO-8859-1"; // "English_Australian", "Western Europe & US"
 
94
    case 0x1009: return "ISO-8859-1"; // "English_Canadian", "Western Europe & US"
 
95
    case 0x1409: return "ISO-8859-1"; // "English_New_Zealand", "Western Europe & US"
 
96
    case 0x1809: return "ISO-8859-1"; // "English_Irish", "Western Europe & US"
 
97
    case 0x1c09: return "ISO-8859-1"; // "English_South_Africa", "Western Europe & US"
 
98
    case 0x2009: return "ISO-8859-1"; // "English_Jamaica", "Western Europe & US"
 
99
    case 0x2409: return "ISO-8859-1"; // "English_Caribbean", "Western Europe & US"
 
100
    case 0x2809: return "ISO-8859-1"; // "English_Belize", "Western Europe & US"
 
101
    case 0x2c09: return "ISO-8859-1"; // "English_Trinidad", "Western Europe & US"
 
102
    case 0x3009: return "ISO-8859-1"; // "English_Zimbabwe", "Western Europe & US"
 
103
    case 0x3409: return "ISO-8859-1"; // "English_Philippines", "Western Europe & US"
 
104
    case 0x0425: return "ISO-8859-13"; //"Estonian", "Baltic"
 
105
    case 0x0438: return "ISO-8859-1"; // "Faeroese", "Western Europe & US"
 
106
    case 0x0429: return "ISO-8859-6"; // "Farsi", "Arabic"
 
107
    case 0x040b: return "ISO-8859-1"; // "Finnish", "Western Europe & US"
 
108
    case 0x040c: return "ISO-8859-1"; // "French_Standard", "Western Europe & US"
 
109
    case 0x080c: return "ISO-8859-1"; // "French_Belgian", "Western Europe & US"
 
110
    case 0x0c0c: return "ISO-8859-1"; // "French_Canadian", "Western Europe & US"
 
111
    case 0x100c: return "ISO-8859-1"; // "French_Swiss", "Western Europe & US"
 
112
    case 0x140c: return "ISO-8859-1"; // "French_Luxembourg", "Western Europe & US"
 
113
    case 0x180c: return "ISO-8859-1"; // "French_Monaco", "Western Europe & US"
 
114
    case 0x0437: return null; //        "Georgian", "Georgian"
 
115
    case 0x0407: return "ISO-8859-1"; // "German_Standard", "Western Europe & US"
 
116
    case 0x0807: return "ISO-8859-1"; // "German_Swiss", "Western Europe & US"
 
117
    case 0x0c07: return "ISO-8859-1"; // "German_Austrian", "Western Europe & US"
 
118
    case 0x1007: return "ISO-8859-1"; // "German_Luxembourg", "Western Europe & US"
 
119
    case 0x1407: return "ISO-8859-1"; // "German_Liechtenstein", "Western Europe & US"
 
120
    case 0x0408: return "ISO-8859-7"; // "Greek", "Greek"
 
121
    case 0x040d: return "ISO-8859-8"; // "Hebrew", "Hebrew"
 
122
    case 0x0439: return null; //        "Hindi", "Indic"
 
123
    case 0x040e: return "ISO-8859-2"; // "Hungarian", "Central Europe"
 
124
    case 0x040f: return "ISO-8859-1"; // "Icelandic", "Western Europe & US"
 
125
    case 0x0421: return "ISO-8859-1"; // "Indonesian", "Western Europe & US"
 
126
    case 0x0410: return "ISO-8859-1"; // "Italian_Standard", "Western Europe & US"
 
127
    case 0x0810: return "ISO-8859-1"; // "Italian_Swiss", "Western Europe & US"
 
128
    case 0x0411: return "cp932"; //     "Japanese", "Japanese"
 
129
    case 0x043f: return "cp1251"; //    "Kazakh", "Cyrillic"
 
130
    case 0x0457: return null; //        "Konkani", "Indic"
 
131
    case 0x0412: return "cp949"; //     "Korean", "Korean"
 
132
    case 0x0426: return "ISO-8859-13"; //"Latvian", "Baltic"
 
133
    case 0x0427: return "ISO-8859-13"; //"Lithuanian", "Baltic"
 
134
    case 0x042f: return "cp1251"; //    "Macedonian", "Cyrillic"
 
135
    case 0x043e: return "ISO-8859-1"; // "Malay_Malaysia", "Western Europe & US"
 
136
    case 0x083e: return "ISO-8859-1"; // "Malay_Brunei_Darussalam", "Western Europe & US"
 
137
    case 0x044e: return null; //        "Marathi", "Indic"
 
138
    case 0x0414: return "ISO-8859-1"; // "Norwegian_Bokmal", "Western Europe & US"
 
139
    case 0x0814: return "ISO-8859-1"; // "Norwegian_Nynorsk", "Western Europe & US"
 
140
    case 0x0415: return "ISO-8859-2"; // "Polish", "Central Europe"
 
141
    case 0x0416: return "ISO-8859-1"; // "Portuguese_Brazilian", "Western Europe & US"
 
142
    case 0x0816: return "ISO-8859-1"; // "Portuguese_Standard", "Western Europe & US"
 
143
    case 0x0418: return "ISO-8859-2"; // "Romanian", "Central Europe"
 
144
    case 0x0419: return "cp1251"; //    "Russian", "Cyrillic"
 
145
    case 0x044f: return null; //        "Sanskrit", "Indic"
 
146
    case 0x081a: return "ISO-8859-2"; // "Serbian_Latin", "Central Europe"
 
147
    case 0x0c1a: return "cp1251"; //    "Serbian_Cyrillic", "Cyrillic"
 
148
    case 0x041b: return "ISO-8859-2"; // "Slovak", "Central Europe"
 
149
    case 0x0424: return "ISO-8859-2"; // "Slovenian", "Central Europe"
 
150
    case 0x040a: return "ISO-8859-1"; // "Spanish_Trad_Sort", "Western Europe & US"
 
151
    case 0x080a: return "ISO-8859-1"; // "Spanish_Mexican", "Western Europe & US"
 
152
    case 0x0c0a: return "ISO-8859-1"; // "Spanish_Modern_Sort", "Western Europe & US"
 
153
    case 0x100a: return "ISO-8859-1"; // "Spanish_Guatemala", "Western Europe & US"
 
154
    case 0x140a: return "ISO-8859-1"; // "Spanish_Costa_Rica", "Western Europe & US"
 
155
    case 0x180a: return "ISO-8859-1"; // "Spanish_Panama", "Western Europe & US"
 
156
    case 0x1c0a: return "ISO-8859-1"; // "Spanish_Dominican_Repub", "Western Europe & US"
 
157
    case 0x200a: return "ISO-8859-1"; // "Spanish_Venezuela", "Western Europe & US"
 
158
    case 0x240a: return "ISO-8859-1"; // "Spanish_Colombia", "Western Europe & US"
 
159
    case 0x280a: return "ISO-8859-1"; // "Spanish_Peru", "Western Europe & US"
 
160
    case 0x2c0a: return "ISO-8859-1"; // "Spanish_Argentina", "Western Europe & US"
 
161
    case 0x300a: return "ISO-8859-1"; // "Spanish_Ecuador", "Western Europe & US"
 
162
    case 0x340a: return "ISO-8859-1"; // "Spanish_Chile", "Western Europe & US"
 
163
    case 0x380a: return "ISO-8859-1"; // "Spanish_Uruguay", "Western Europe & US"
 
164
    case 0x3c0a: return "ISO-8859-1"; // "Spanish_Paraguay", "Western Europe & US"
 
165
    case 0x400a: return "ISO-8859-1"; // "Spanish_Bolivia", "Western Europe & US"
 
166
    case 0x440a: return "ISO-8859-1"; // "Spanish_El_Salvador", "Western Europe & US"
 
167
    case 0x480a: return "ISO-8859-1"; // "Spanish_Honduras", "Western Europe & US"
 
168
    case 0x4c0a: return "ISO-8859-1"; // "Spanish_Nicaragua", "Western Europe & US"
 
169
    case 0x500a: return "ISO-8859-1"; // "Spanish_Puerto_Rico", "Western Europe & US"
 
170
    case 0x0441: return "ISO-8859-1"; // "Swahili", "Western Europe & US"
 
171
    case 0x041d: return "ISO-8859-1"; // "Swedish", "Western Europe & US"
 
172
    case 0x081d: return "ISO-8859-1"; // "Swedish_Finland", "Western Europe & US"
 
173
    case 0x0449: return null; //        "Tamil", "Indic"
 
174
    case 0x0444: return "cp1251"; //    "Tatar", "Cyrillic"
 
175
    case 0x041e: return "ISO-8859-11"; //"Thai", "Thai"
 
176
    case 0x041f: return "ISO-8859-9"; // "Turkish", "Turkish"
 
177
    case 0x0422: return "cp1251"; //    "Ukrainian", "Cyrillic"
 
178
    case 0x0420: return "ISO-8859-6"; // "Urdu", "Arabic"
 
179
    case 0x0443: return "ISO-8859-9"; // "Uzbek_Latin", "Turkish"
 
180
    case 0x0843: return "cp1251"; //    "Uzbek_Cyrillic", "Cyrillic"
 
181
    case 0x042a: return null; //        "Vietnamese", "Vietnamese"
 
182
    }
 
183
    return 'UTF-8';
 
184
}
 
185
 
 
186
var utf8Converter = Cc["@mozilla.org/intl/utf8converterservice;1"].getService(Ci.nsIUTF8ConverterService);
 
187
 
 
188
function nativeToUtf8(nativeString, lcid) {
 
189
    var charset = getCharsetFromLcid(lcid);
 
190
    if (! charset) {
 
191
        return nativeString;
 
192
    }
 
193
    return utf8Converter.convertStringToUTF8(nativeString, charset, 0);
 
194
}
 
195
 
 
196
var Lib = function(libPath) {
 
197
    if (! libPath) {
 
198
        libPath = ioService.newURI('resource://chmfox-lib', null, null)
 
199
            .QueryInterface(Ci.nsIFileURL).file.path;
 
200
    }
 
201
 
 
202
    this._libraryPath = libPath;
 
203
    this._library = ctypes.open(this._libraryPath);
 
204
 
 
205
    this.CHM_RESOLVE_SUCCESS = 0;
 
206
    this.CHM_RESOLVE_FAILURE = 1;
 
207
 
 
208
    this.CHM_UNCOMPRESSED = 0;
 
209
    this.CHM_COMPRESSED = 1;
 
210
 
 
211
    this.CHM_ENUMERATE_NORMAL = 1;
 
212
    this.CHM_ENUMERATE_META = 2;
 
213
    this.CHM_ENUMERATE_SPECIAL = 4;
 
214
    this.CHM_ENUMERATE_FILES = 8;
 
215
    this.CHM_ENUMERATE_DIRS = 16;
 
216
    this.CHM_ENUMERATE_ALL = 31;
 
217
    this.CHM_ENUMERATOR_FAILURE = 0;
 
218
 
 
219
    this.CHM_ENUMERATOR_CONTINUE = 1;
 
220
    this.CHM_ENUMERATOR_SUCCESS = 2;
 
221
 
 
222
    this.CHM_MAX_PATH_LENGTH = 512;
 
223
 
 
224
    this.chmFilePtr = ctypes.StructType("chmFile").ptr;
 
225
    this.chmUnitInfo = ctypes.StructType(
 
226
        'chmUnitInfo', [
 
227
            {'start': ctypes.uint64_t},
 
228
            {'length': ctypes.uint64_t},
 
229
            {'space': ctypes.int},
 
230
            {'flags': ctypes.int},
 
231
            {'path': ctypes.char.array(this.CHM_MAX_PATH_LENGTH+1)}
 
232
        ]);
 
233
 
 
234
    this.enumerator = ctypes.FunctionType(
 
235
        ctypes.default_abi,
 
236
        ctypes.int, [
 
237
            this.chmFilePtr,
 
238
            this.chmUnitInfo.ptr,
 
239
            ctypes.voidptr_t]).ptr;
 
240
 
 
241
    this.search_enumerator = ctypes.FunctionType(
 
242
        ctypes.default_abi,
 
243
        ctypes.int, [
 
244
            this.chmFilePtr,
 
245
            ctypes.char.ptr,
 
246
            ctypes.char.ptr]).ptr;
 
247
 
 
248
    var openCharType = ctypes.char.ptr;
 
249
    if ('WINNT' == xulRuntime.OS) {
 
250
        openCharType = ctypes.jschar.ptr;
 
251
    }
 
252
 
 
253
    this.open = this._library.declare(
 
254
        'chmfox_open', ctypes.default_abi,
 
255
        this.chmFilePtr,
 
256
        openCharType);
 
257
 
 
258
    this.close = this._library.declare(
 
259
        'chmfox_close', ctypes.default_abi,
 
260
        ctypes.void_t,
 
261
        this.chmFilePtr);
 
262
 
 
263
    this.set_param = this._library.declare(
 
264
        'chmfox_set_param', ctypes.default_abi,
 
265
        ctypes.void_t,
 
266
        this.chmFilePtr, ctypes.int, ctypes.int);
 
267
 
 
268
    this.resolve_object = this._library.declare(
 
269
        'chmfox_resolve_object', ctypes.default_abi,
 
270
        ctypes.int,
 
271
        this.chmFilePtr, ctypes.char.ptr, this.chmUnitInfo.ptr);
 
272
 
 
273
    this.retrieve_object = this._library.declare(
 
274
        'chmfox_retrieve_object', ctypes.default_abi,
 
275
        ctypes.int64_t,
 
276
        this.chmFilePtr, this.chmUnitInfo.ptr, ctypes.unsigned_char.ptr,
 
277
        ctypes.uint64_t, ctypes.int64_t);
 
278
 
 
279
    this.enumerate = this._library.declare(
 
280
        'chmfox_enumerate', ctypes.default_abi,
 
281
        ctypes.int,
 
282
        this.chmFilePtr, ctypes.int, this.enumerator, ctypes.voidptr_t);
 
283
 
 
284
    this.enumerate_dir = this._library.declare(
 
285
        'chmfox_enumerate_dir', ctypes.default_abi,
 
286
        ctypes.int,
 
287
        this.chmFilePtr, ctypes.char.ptr, ctypes.int, this.enumerator, ctypes.voidptr_t);
 
288
 
 
289
    this.search = this._library.declare(
 
290
        'chmfox_search', ctypes.default_abi,
 
291
        ctypes.int,
 
292
        this.chmFilePtr, ctypes.char.ptr, ctypes.int, ctypes.int, this.search_enumerator);
 
293
 
 
294
    // extensions
 
295
    this.find_ext = function(handle, ext, where) {
 
296
        if (! where) {
 
297
            where = '/';
 
298
        }
 
299
 
 
300
        var result = null;
 
301
        let compare = function(handle, ui, context) {
 
302
            var path = ui.contents.path.readString();
 
303
            if (path.substr(path.length - ext.length) == ext) {
 
304
                result = path;
 
305
                return this.CHM_ENUMERATOR_SUCCESS;
 
306
            }
 
307
            return this.CHM_ENUMERATOR_CONTINUE;
 
308
        };
 
309
 
 
310
        let callback = this.enumerator(compare);
 
311
        var n = ctypes.voidptr_t();
 
312
        this.enumerate_dir(handle, where, this.CHM_ENUMERATE_NORMAL,
 
313
                          callback, null);
 
314
        return result;
 
315
    };
 
316
 
 
317
    this.search_all = function(handle, keywords, whole_words, titles_only) {
 
318
        var result = [];
 
319
        let store = function(handle, topic, url) {
 
320
            result.push([topic.readString(), url.readString()]);
 
321
            return this.CHM_ENUMERATOR_CONTINUE;
 
322
        };
 
323
 
 
324
        let callback = this.search_enumerator(store);
 
325
        this.search(handle, keywords, whole_words, titles_only, callback);
 
326
        return result;
 
327
    };
 
328
 
 
329
    return this;
 
330
};
 
331
 
 
332
var lib = new Lib();
 
333
 
 
334
function getString(array, index) {
 
335
    var out = '';
 
336
    while (true) {
 
337
        var i = array[index++];
 
338
        if (i == 0) {
 
339
            break;
 
340
        }
 
341
        out += String.fromCharCode(i);
 
342
    }
 
343
    return out;
 
344
    // return ctypes.cast(array.addressOfElement(index), ctypes.char.ptr).readString();
 
345
}
 
346
 
 
347
function getBuffer(array, index, len) {
 
348
    var out = '';
 
349
    var end = index + len;
 
350
    for (var i = index; i < end; ++ i) {
 
351
        out += String.fromCharCode(array[i]);
 
352
    }
 
353
    return out;
 
354
}
 
355
 
 
356
function getUInt32(array, index) {
 
357
    return ctypes.cast(array.addressOfElement(index), ctypes.uint32_t.ptr).contents;
 
358
}
 
359
 
 
360
function getUInt64(array, index) {
 
361
    return ctypes.cast(array.addressOfElement(index), ctypes.uint64_t.ptr).contents;
 
362
}
 
363
 
 
364
function prependSlash(str) {
 
365
    if (str && str[0] != '/') {
 
366
        return '/' + str;
 
367
    }
 
368
    return str;
 
369
}
 
370
 
 
371
 
 
372
function HtmlizeObject(str) {
 
373
    var r = str.replace(/<OBJECT/ig, '<div')
 
374
        .replace(/<\/OBJECT/ig, '</div')
 
375
        .replace(/<PARAM/ig, '<span')
 
376
        .replace(/<\/PARAM/ig, '</span');
 
377
    return r;
 
378
    // var i = r.indexOf('<UL>');
 
379
    // var j = r.lastIndexOf('</BODY>');
 
380
    // log(i + ' ' + j);
 
381
    // r = r.substring(i, j-1);
 
382
    // return r;
 
383
}
 
384
 
 
385
var ChmFile = function(path, uri) {
 
386
    this.path = path;
 
387
    this.uri = uri;
 
388
    log('open chm file:'+ this.path);
 
389
    this.handle = lib.open(this.path);
 
390
 
 
391
    this.close = function() {
 
392
        lib.close(this.handle);
 
393
        log('closed chm file:' + this.path);
 
394
    };
 
395
 
 
396
    this.isValid = function() {
 
397
        return !this.handle.isNull();
 
398
    };
 
399
 
 
400
    if (! this.isValid()) {
 
401
        log('open chm file failed: ' + this.path);
 
402
        return this;
 
403
    }
 
404
 
 
405
    this.getSystemInfo = function () {
 
406
        var ui = lib.chmUnitInfo();
 
407
        if (lib.CHM_RESOLVE_FAILURE == lib.resolve_object(this.handle, '/#SYSTEM', ui.address())) {
 
408
            return ;
 
409
        }
 
410
 
 
411
        var buffer = ctypes.unsigned_char.array(ui.length)();
 
412
        var length = lib.retrieve_object(
 
413
            this.handle, ui.address(),
 
414
            buffer.addressOfElement(0),
 
415
            4, buffer.length);
 
416
        if (length == 0) {
 
417
            return;
 
418
        }
 
419
 
 
420
        this.index = '';
 
421
        this.topics = '';
 
422
        this.home = '';
 
423
        this.lcid = null;
 
424
        this.title = '';
 
425
 
 
426
        var index = 0;
 
427
        while (index < length) {
 
428
            var type = buffer[index] + (buffer[index+1] * 256);
 
429
            index += 2;
 
430
            var len = buffer[index] + (buffer[index+1] * 256);
 
431
            index += 2;
 
432
            switch(type) {
 
433
                case 0:
 
434
                    this.topics = prependSlash(getString(buffer, index, len));
 
435
                    break;
 
436
                case 1:
 
437
                    this.index = prependSlash(getString(buffer, index, len));
 
438
                    break;
 
439
                case 2:
 
440
                    this.home = prependSlash(getString(buffer, index, len));
 
441
                    break;
 
442
                case 3:
 
443
                    this.title = prependSlash(getString(buffer, index, len));
 
444
                    break;
 
445
                case 4:
 
446
                    this.lcid = getUInt32(buffer, index);
 
447
                    this.use_dbcs = getUInt32(buffer, index+0x4);
 
448
                    this.searchable = getUInt32(buffer, index+0x8);
 
449
                    this.has_klinks = getUInt32(buffer, index+0xc);
 
450
                    this.has_alinks = getUInt32(buffer, index+0x10);
 
451
                    this.timestamp = getUInt64(buffer, index+0x14);
 
452
                    break;
 
453
                case 5: // Always "main"?
 
454
                    this.default_window = getString(buffer, index, len);
 
455
                case 6: // Project name
 
456
                    this.project = getString(buffer, index, len);
 
457
                case 7:
 
458
                    this.has_binary_index = getUInt32(buffer, index);
 
459
                    break;
 
460
                case 9: // Encoder
 
461
                    this.compiled_by = getString(buffer, index, len);
 
462
                    break;
 
463
                case 10: // Unknown
 
464
                    break;
 
465
                case 11:
 
466
                    this.has_binary_toc = getUInt32(buffer, index);
 
467
                    break;
 
468
                case 12: // Unknown
 
469
                case 13: // Unknown
 
470
                case 15: // Unknown
 
471
                    break;
 
472
                case 16:
 
473
                    this.encoding = getBuffer(buffer, index, len);
 
474
                    break;
 
475
            }
 
476
            index += len;
 
477
        }
 
478
 
 
479
        this.topics = nativeToUtf8(this.topics, this.lcid);
 
480
        this.index = nativeToUtf8(this.index, this.lcid);
 
481
        this.home = nativeToUtf8(this.home, this.lcid);
 
482
        this.title = nativeToUtf8(this.title, this.lcid);
 
483
        this.project = nativeToUtf8(this.project, this.lcid);
 
484
        this.compiled_by = nativeToUtf8(this.compiled_by, this.lcid);
 
485
 
 
486
        // Gets information from the #WINDOWS file.
 
487
        // Checks the #WINDOWS file to see if it has any info that was
 
488
        // not found in #SYSTEM (topics, index or default page.
 
489
        if (lib.CHM_RESOLVE_FAILURE == lib.resolve_object(this.handle, '/#WINDOWS', ui.address())) {
 
490
            return;
 
491
        }
 
492
 
 
493
        const WINDOWS_HEADER_LENGTH = 8;
 
494
        buffer = ctypes.unsigned_char.array(WINDOWS_HEADER_LENGTH)();
 
495
        length = lib.retrieve_object(
 
496
            this.handle, ui.address(),
 
497
            buffer.addressOfElement(0), 0, buffer.length);
 
498
        if (length < buffer.length) {
 
499
            return;
 
500
        }
 
501
        var entries = getUInt32(buffer, 0);
 
502
        var entry_size = getUInt32(buffer, 4);
 
503
        buffer = ctypes.unsigned_char.array(entries*entry_size)();
 
504
        length = lib.retrieve_object(
 
505
            this.handle, ui.address(),
 
506
            buffer.addressOfElement(0), WINDOWS_HEADER_LENGTH, buffer.length);
 
507
 
 
508
        if (length == 0) {
 
509
            return;
 
510
        }
 
511
 
 
512
        if (lib.resolve_object(this.handle, "/#STRINGS", ui.address()) != lib.CHM_RESOLVE_SUCCESS) {
 
513
            return;
 
514
        }
 
515
 
 
516
        var size = 0;
 
517
        var factor_buffer = ctypes.unsigned_char.array(4096)();
 
518
 
 
519
        for (var i = 0; i < entries; ++i) {
 
520
            var offset = i * entry_size;
 
521
            var off_title = getUInt32(buffer, offset + 0x14);
 
522
            var off_home = getUInt32(buffer, offset + 0x68);
 
523
            var off_hhc = getUInt32(buffer, offset + 0x60);
 
524
            var off_hhk = getUInt32(buffer, offset + 0x64);
 
525
            var factor = Math.floor(off_title / 4096);
 
526
 
 
527
            if (size == 0) {
 
528
                size = lib.retrieve_object(
 
529
                    this.handle, ui.address(),
 
530
                    factor_buffer.addressOfElement(0),
 
531
                    factor*4096, factor_buffer.length);
 
532
            }
 
533
 
 
534
            if (size > 0 && off_title && this.title) {
 
535
                this.title = getString(factor_buffer, off_title % 4096, ui.length);
 
536
            }
 
537
 
 
538
                        if (factor != Math.floor(off_home / 4096)) {
 
539
                factor = Math.floor(off_home / 4096);
 
540
                size = lib.retrieve_object(
 
541
                    this.handle, ui.address(),
 
542
                    factor_buffer.addressOfElement(0),
 
543
                    factor*4096, factor_buffer.length);
 
544
                        }
 
545
 
 
546
            if (size > 0 && off_home && !this.home) {
 
547
                this.home = prependSlash(getString(factor_buffer, off_home % 4096, ui.length));
 
548
            }
 
549
 
 
550
            if (factor != Math.floor(off_hhc/4096)) {
 
551
                                factor = Math.floor(off_hhc / 4096);
 
552
                    size = lib.retrieve_object(
 
553
                    this.handle, ui.address(),
 
554
                                        factor_buffer.addressOfElement(0),
 
555
                                        factor * 4096,
 
556
                                        factor_buffer.length);
 
557
                        }
 
558
 
 
559
                        if (size && off_hhc && !this.topics) {
 
560
                this.topics = prependSlash(getString(factor_buffer, off_hhc % 4096, ui.length));
 
561
            }
 
562
 
 
563
            if (factor != Math.floor(off_hhk / 4096)) {
 
564
                                factor = Math.floor(off_hhk / 4096);
 
565
                                size = lib.retrieve_object(
 
566
                    this.handle, ui.address(),
 
567
                                    factor_buffer.addressOfElement(0),
 
568
                                    factor * 4096,
 
569
                                    factor_buffer.length);
 
570
                        }
 
571
 
 
572
                        if(size && off_hhk && !this.index) {
 
573
                this.index = prependSlash(getString(factor_buffer, off_hhk % 4096, ui.length));
 
574
            }
 
575
 
 
576
            log("lcid: " + this.lcid);
 
577
            log("use dbcs: " + this.use_dbcs);
 
578
        };
 
579
 
 
580
    };
 
581
 
 
582
    this.getSystemInfo();
 
583
 
 
584
    this.getTopics = function() {
 
585
        if (this.html_topics) {
 
586
            return this.html_topics;
 
587
        }
 
588
        if (! this.isValid()) {
 
589
            return;
 
590
        }
 
591
        var ui = lib.chmUnitInfo();
 
592
        if (! this.topics) {
 
593
            if (this.project) {
 
594
                var try_topics_page = '/' + this.project + '.hhc';
 
595
                if (lib.CHM_RESOLVE_SUCCESS == lib.resolve_object(
 
596
                        this.handle, try_topics_page, ui.address())) {
 
597
                    this.topics = try_topics_page;
 
598
                }
 
599
            }
 
600
            if (! this.topics) {
 
601
                this.topics = prependSlash(lib.find_ext(this.handle, '.hhc'));
 
602
            }
 
603
            if (! this.topics) {
 
604
                return;
 
605
            }
 
606
        }
 
607
 
 
608
        if (lib.CHM_RESOLVE_SUCCESS == lib.resolve_object(
 
609
            this.handle, this.topics, ui.address())) {
 
610
            var buf = ctypes.unsigned_char.array(Math.floor(ui.length+1))();
 
611
            var r = lib.retrieve_object(
 
612
                this.handle, ui.address(),
 
613
                buf.addressOfElement(0), 0, ui.length);
 
614
            if (r > 0) {
 
615
                this.topics_content = utf8Encode(nativeToUtf8(getString(buf, 0), this.lcid));
 
616
                this.html_topics = HtmlizeObject(this.topics_content);
 
617
            }
 
618
        }
 
619
        return this.html_topics;
 
620
    };
 
621
 
 
622
    this.getIndex = function() {
 
623
        if (this.html_index) {
 
624
            return this.html_index;
 
625
        }
 
626
        var ui = lib.chmUnitInfo();
 
627
        if (! this.index) {
 
628
            if (this.project) {
 
629
                var try_index_page = '/' + this.project + '.hhk';
 
630
                if (lib.CHM_RESOLVE_SUCCESS == lib.resolve_object(
 
631
                        this.handle, try_index_page, ui.address())) {
 
632
                    this.topics = try_index_page;
 
633
                }
 
634
            }
 
635
            if (! this.index) {
 
636
                this.index = prependSlash(lib.find_ext(this.handle, '.hhk'));
 
637
            }
 
638
            if (! this.index) {
 
639
                return;
 
640
            }
 
641
        }
 
642
        if (lib.CHM_RESOLVE_SUCCESS == lib.resolve_object(
 
643
                this.handle, this.index, ui.address())) {
 
644
            var buf = ctypes.unsigned_char.array(Math.floor(ui.length+1))();
 
645
            var r = lib.retrieve_object(
 
646
                this.handle, ui.address(),
 
647
                buf.addressOfElement(0), 0, ui.length);
 
648
            if (r > 0) {
 
649
                this.index_content = utf8Encode(nativeToUtf8(getBuffer(buf, 0, r), this.lcid));
 
650
                this.html_index = HtmlizeObject(this.index_content);
 
651
            }
 
652
        }
 
653
        return this.html_index;
 
654
    };
 
655
 
 
656
    this.getContent = function(page) {
 
657
        var ui = lib.chmUnitInfo();
 
658
        if (lib.CHM_RESOLVE_SUCCESS != lib.resolve_object(this.handle, page, ui.address())) {
 
659
            log('page not found: ' + this.path + ' ' + page);
 
660
            return;
 
661
        }
 
662
        var buffer = ctypes.unsigned_char.array(Math.floor(ui.length+1))();
 
663
        var length = lib.retrieve_object(
 
664
                this.handle, ui.address(),
 
665
                buffer.addressOfElement(0), 0, ui.length);
 
666
        if (length > 0) {
 
667
            return getBuffer(buffer, 0, length);
 
668
        }
 
669
        else {
 
670
            log('page retrieve failed: ' + this.path + ' ' + page);
 
671
        }
 
672
        return;
 
673
    };
 
674
 
 
675
    return this;
 
676
};
 
677
 
 
678
function normlizePath(path) {
 
679
    var parts = path.split('/');
 
680
    var norm = [];
 
681
    for (var i = 0; i < parts.length; ++i) {
 
682
        switch(parts[i]) {
 
683
        case '.':
 
684
        case '':
 
685
            break;
 
686
        case '..':
 
687
            if (norm.length != 0) norm.pop();
 
688
            break;
 
689
      default:
 
690
        norm.push(parts[i]);
 
691
      }
 
692
    }
 
693
    return '/' + norm.join('/');
 
694
}
 
695
 
 
696
function redirect(to, orig) {
 
697
    var html = '<html><head><meta http-equiv="refresh" content="0; url=' +
 
698
                   utf8Encode(to) + '" /></head></html>';
 
699
    var sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
 
700
    sis.setData(html, html.length);
 
701
    var isc = Cc["@mozilla.org/network/input-stream-channel;1"].createInstance(Ci.nsIInputStreamChannel);
 
702
    isc.contentStream = sis;
 
703
    isc.setURI(orig);
 
704
 
 
705
    var bc = isc.QueryInterface(Ci.nsIChannel);
 
706
    bc.contentCharset = 'utf-8';
 
707
    bc.contentType = "text/html";
 
708
    bc.contentLength = html.length;
 
709
    bc.originalURI = orig;
 
710
    // bc.owner = this;
 
711
    return bc;
 
712
}
 
713
 
 
714
function getChmFileAndModifyUri(uri) {
 
715
    var urlParts = decodeURI(uri.spec).split('!');
 
716
    var chm_uri = urlParts[0];
 
717
    var url = chm_uri.substring(4); //Remove "chm:"
 
718
    url = "file:" + url;
 
719
    url = unescape(url);
 
720
    url = url.replace('\\', '/');
 
721
    url = ioService.newURI(url, null, null);
 
722
    var chm = Application.storage.get(chm_uri, null);
 
723
    if (! chm) {
 
724
        var local_file = url.QueryInterface(Ci.nsIFileURL).file.path;
 
725
        chm = new ChmFile(local_file, chm_uri);
 
726
        if (! chm.isValid()) {
 
727
            // @todo should use firefox default handle for file not found
 
728
            uri = ioService.newURI("about:blank", null, null);
 
729
            return ioService.newChannelFromURI(uri);
 
730
        }
 
731
        Application.storage.set(chm_uri, chm);
 
732
    }
 
733
 
 
734
    var pagepath = null;
 
735
 
 
736
    if (urlParts.length == 1) {
 
737
        urlParts.push(chm.home);
 
738
        uri = ioService.newURI(urlParts.join('!'), null, null);
 
739
    }
 
740
    else if (urlParts[1] == '/' || urlParts[1] == '') {
 
741
        urlParts[1] = chm.home;
 
742
        uri = ioService.newURI(urlParts.join('!'), null, null);        
 
743
    }
 
744
    else {
 
745
        pagepath = urlParts[1];
 
746
    }
 
747
 
 
748
    return {
 
749
        'file':chm,
 
750
        'page':pagepath,
 
751
        'uri':uri,
 
752
        'path':url.spec};
 
753
}
 
754
 
 
755
function Protocol() {
 
756
}
 
757
 
 
758
Protocol.prototype = {
 
759
  scheme: kScheme,
 
760
  classDescription: "CHM Protocol",
 
761
  classID: Components.ID("c152fc51-a5bf-4cc7-99f1-66ca8459d806"),
 
762
  contractID: "@mozilla.org/network/protocol;1?name=" + kScheme,
 
763
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler, Ci.nsISupports]),
 
764
 
 
765
  defaultPort: -1,
 
766
  protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
 
767
        Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
 
768
        Ci.nsIProtocolHandler.URI_IS_LOCAL_FILE | 
 
769
        Ci.nsIProtocolHandler.URI_NOAUTH,
 
770
 
 
771
  allowPort: function(port, scheme) {
 
772
    return false;
 
773
  },
 
774
 
 
775
  newURI: function(spec, charset, baseURI) {
 
776
      // FIXME: why there is 'chm:' uri
 
777
      if (spec == 'chm:') {
 
778
          return undefined;
 
779
      }
 
780
 
 
781
      // Remote websites are not allowed to load or link to local files
 
782
      if (baseURI) {
 
783
          var baseUriProtocol = baseURI.spec.substr(0, baseURI.spec.indexOf(':')).toLowerCase(); 
 
784
          if (baseUriProtocol != 'chm' && baseUriProtocol != 'chrome') {
 
785
              return undefined;
 
786
          }
 
787
      }
 
788
 
 
789
    var uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
 
790
    if (spec.substring(0, 1) == "#") {
 
791
        var basespec = baseURI.spec;
 
792
        var pos = basespec.indexOf("#");
 
793
        if (pos > 0) {
 
794
            basespec = basespec.substring(0, pos);
 
795
        }
 
796
        uri.spec = basespec + spec;
 
797
    }
 
798
 
 
799
    else if (spec.indexOf(":") > 0) {
 
800
        uri.spec = spec;
 
801
    }
 
802
 
 
803
    else if (spec.substring(0, 1) != '/') {
 
804
        var basespec = baseURI.spec;
 
805
        var pos = basespec.lastIndexOf("!/");
 
806
        if (pos > 0) {
 
807
            var pagepath = basespec.substring(pos + 1, basespec.lastIndexOf('/') + 1) + spec;
 
808
            if (pagepath.lastIndexOf('/') >= 1)
 
809
                pagepath = normlizePath(pagepath);
 
810
            uri.spec = basespec.substring(0, pos + 1) + pagepath;
 
811
        } else
 
812
            uri.spec = basespec + "!/" + spec;
 
813
    }
 
814
 
 
815
    getChmFileAndModifyUri(uri);
 
816
    return uri;
 
817
  },
 
818
 
 
819
  newChannel: function(aURI) {
 
820
    var chm = getChmFileAndModifyUri(aURI);
 
821
    if (chm.file) {
 
822
        if (! chm.page) {
 
823
            return redirect(chm.uri.spec, aURI);
 
824
        }
 
825
 
 
826
        if (chm.page == "/#HHC") {
 
827
            return this.newRawTopicsChannel(aURI, chm.file);
 
828
        }
 
829
 
 
830
        if (chm.page == "/#HHK") {
 
831
            return this.newRawIndexChannel(aURI, chm.file);
 
832
        }
 
833
 
 
834
        var pos = chm.page.indexOf("#");
 
835
        if (pos > 0) {
 
836
            chm.page = chm.page.substring(0, pos);
 
837
        }
 
838
    }
 
839
 
 
840
    // Create the channel
 
841
    var mime = "text/html";
 
842
    if (chm.page) {
 
843
        var pos = chm.page.lastIndexOf(".");
 
844
        if (pos > 0) {
 
845
            var ext = chm.page.substring(pos + 1);
 
846
            switch (ext.toLowerCase()) {
 
847
            case "svg":
 
848
                mime = "image/svg+xml";
 
849
                break;
 
850
            case "gif":
 
851
                mime = "image/gif";
 
852
                break;
 
853
            case "jpg":
 
854
            case "jpeg":
 
855
            case "jpe":
 
856
                mime = "image/jpeg";
 
857
                break;
 
858
            case "png":
 
859
                mime = "image/png";
 
860
                break;
 
861
            case "css":
 
862
                mime = "text/css";
 
863
                break;
 
864
            case "mht":
 
865
                mime = "message/rfc822";
 
866
            case "txt":
 
867
                mime = "text/plain";
 
868
                break;
 
869
            case "xml":
 
870
                mime = "text/xml";
 
871
                break;
 
872
            case "xhtml":
 
873
                mime = "text/xhtml";
 
874
                break;
 
875
            case 'html':
 
876
            case 'htm':
 
877
                mime = 'text/html';
 
878
                break;
 
879
            case 'bmp':
 
880
                mime = 'image/bitmap';
 
881
                break;
 
882
            default:
 
883
                mime = "application/octet-stream";
 
884
            }
 
885
        }
 
886
    }
 
887
 
 
888
    var content = undefined;
 
889
    if (chm.file) {
 
890
        content = chm.file.getContent(chm.page);
 
891
        if (! content) {
 
892
            content = chm.file.path + '! ' + chm.path + '  Not Found!';
 
893
        }
 
894
    }
 
895
    else {
 
896
        var filePath = decodeURI(aURI.spec).substr(6);
 
897
        filePath = filePath.split('!')[0];
 
898
        content = "CHM file [" + filePath + "] open failed!";
 
899
    }
 
900
      
 
901
    var is = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
 
902
    is.setData(content, content.length);
 
903
    var isc = Cc["@mozilla.org/network/input-stream-channel;1"].createInstance(Ci.nsIInputStreamChannel);
 
904
    isc.contentStream = is;
 
905
    isc.setURI(aURI);
 
906
 
 
907
    var bc = isc.QueryInterface(Ci.nsIChannel);
 
908
    bc.contentType = mime;
 
909
    // The encoding is in the HTML header
 
910
    // bc.contentCharset = 'utf-8';
 
911
    bc.contentLength = content.length;
 
912
    bc.originalURI = aURI;
 
913
    // bc.owner = this;
 
914
 
 
915
    return bc;
 
916
  },
 
917
 
 
918
  newRawIndexChannel: function(aURI, chm) {
 
919
    var content = chm.getIndex();
 
920
    if (! content) {
 
921
        content = '';
 
922
    }
 
923
    var sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
 
924
    sis.setData(content, content.length);
 
925
 
 
926
    var isc = Cc["@mozilla.org/network/input-stream-channel;1"].createInstance(Ci.nsIInputStreamChannel);
 
927
    isc.contentStream = sis;
 
928
    isc.setURI(aURI);
 
929
 
 
930
    var bc = isc.QueryInterface(Ci.nsIChannel);
 
931
    bc.contentCharset = 'utf-8';
 
932
    bc.contentType = "text/html";
 
933
    bc.contentLength = content.length;
 
934
    bc.originalURI = aURI;
 
935
    // bc.owner = this;
 
936
 
 
937
    return bc;
 
938
  },
 
939
 
 
940
  newRawTopicsChannel: function(aURI, chm) {
 
941
    var content = chm.getTopics();
 
942
    if (! content) {
 
943
        content = '';
 
944
    }
 
945
 
 
946
    var sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
 
947
    sis.setData(content, content.length);
 
948
 
 
949
    var isc = Cc["@mozilla.org/network/input-stream-channel;1"].createInstance(Ci.nsIInputStreamChannel);
 
950
    isc.contentStream = sis;
 
951
    isc.setURI(aURI);
 
952
 
 
953
    var bc = isc.QueryInterface(Ci.nsIChannel);
 
954
    bc.contentCharset = 'utf-8';
 
955
    bc.contentType = "text/html";
 
956
    bc.contentLength = content.length;
 
957
    bc.originalURI = aURI;
 
958
    // bc.owner = this;
 
959
 
 
960
    return bc;
 
961
  }
 
962
};
 
963
 
 
964
 
 
965
function chmXURIContentListener() {
 
966
    this.loadCookie = null;
 
967
    this.parentContentListener = null;
 
968
    this.wrappedJSObject = this;
 
969
}
 
970
 
 
971
chmXURIContentListener.prototype = {
 
972
    classDescription: "chmXURIContentListener",
 
973
    classID: Components.ID("{1c811fec-ec47-45ea-a395-c70fb1fc8f9d}"),
 
974
    contractID: "@zhuoqiang.me/chmXURIContentListener;1",
 
975
    _xpcom_categories: [{ category: "app-startup" }],
 
976
    QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISupports,
 
977
                                           Components.interfaces.nsIURIContentListener,
 
978
                                           Components.interfaces.nsIClassInfo,
 
979
                                           Components.interfaces.nsISupportsWeakReference,
 
980
                                           Components.interfaces.nsIObserver]),
 
981
 
 
982
    canHandleContent: function(aContentType, aIsContentPreferred) {
 
983
        // it is chemical/x-chemdraw on Ubuntu
 
984
        // if (aContentType == "application/octet-stream") {
 
985
        //     return true;
 
986
        // }
 
987
        return true;
 
988
    },
 
989
 
 
990
    doContent: function(aContentType, aIsContentPreferred, aRequest, aContentHandler) {
 
991
 
 
992
        // it is chemical/x-chemdraw on Ubuntu
 
993
        if (true || aContentType == "application/octet-stream") {
 
994
            if (aRequest.name.substr(0,5).toLowerCase() == 'file:' && aRequest.name.substr(-4).toLowerCase() == '.chm') {
 
995
                var urispec = "chm" + aRequest.name.substr(4);
 
996
                urispec = decodeURI(urispec);
 
997
                try {
 
998
                    var lastPosition = prefs.getCharPref("lastPosition."+urispec);
 
999
                    if (lastPosition) {
 
1000
                        urispec = urispec + '!/' + lastPosition;
 
1001
                    }
 
1002
                }
 
1003
                catch (err) {
 
1004
                }
 
1005
                let window = aRequest.loadGroup.notificationCallbacks.
 
1006
                    getInterface(Components.interfaces.nsIDOMWindow);
 
1007
                let webnav = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIWebNavigation);
 
1008
                webnav.loadURI(urispec, 0, null, null, null);
 
1009
                return true;
 
1010
            }
 
1011
        }
 
1012
 
 
1013
        // return false;
 
1014
        throw Components.results.NS_ERROR_NOT_IMPLEMENTED;    // FIXME
 
1015
    },
 
1016
 
 
1017
    isPreferred: function(aContentType) {
 
1018
        if (aContentType == "application/octet-stream") {
 
1019
            return true;
 
1020
        }
 
1021
        return false;
 
1022
    },
 
1023
 
 
1024
    onStartURIOpen: function(aURI) {
 
1025
        return false;
 
1026
    },
 
1027
 
 
1028
    observe: function(subject, topic, data) {
 
1029
        if (topic == "profile-after-change" || topic == "app-startup") {
 
1030
            let uriLoader = Components.classes["@mozilla.org/uriloader;1"].
 
1031
                    getService(Components.interfaces.nsIURILoader);
 
1032
            uriLoader.registerContentListener(contentListener);
 
1033
        }
 
1034
    }
 
1035
};
 
1036
 
 
1037
var contentListener = new chmXURIContentListener();
 
1038
 
 
1039
var components = [chmXURIContentListener];
 
1040
 
 
1041
return {log:log, components: [Protocol, chmXURIContentListener], prefs:prefs};
 
1042
 
 
1043
})();
 
1044
 
 
1045
};
 
1046
 
 
1047
const NSGetFactory = XPCOMUtils.generateNSGetFactory(Chmfox.components);