1 # -*- Mode
: C
++; tab
-width
: 8; indent
-tabs
-mode
: nil
; c
-basic
-offset
: 2 -*- */
2 # ***** BEGIN LICENSE BLOCK
*****
3 # Version
: MPL
1.1/GPL 2.0/LGPL
2.1
5 # The contents
of this file are subject to the Mozilla Public License Version
6 # 1.1 (the
"License"); you may not
use this file except
in compliance
with
7 # the License
. You may obtain a copy
of the License at
8 # http
://www.mozilla.org/MPL/
10 # Software distributed under the License is distributed on an
"AS IS" basis
,
11 # WITHOUT WARRANTY OF ANY KIND
, either express or implied
. See the License
12 # for the specific language governing rights and limitations under the
15 # The Original Code is the Feed Stream Converter
.
17 # The Initial Developer
of the Original Code is Google Inc
.
18 # Portions created by the Initial Developer are
Copyright (C
) 2006
19 # the Initial Developer
. All Rights Reserved
.
22 # Ben Goodger
<beng
@google
.com
>
23 # Jeff Walden
<jwalden
+code
@mit
.edu
>
24 # Will Guaraldi
<will
.guaraldi
@pculture
.org
>
26 # Alternatively
, the contents
of this file may be used under the terms
of
27 # either the GNU General Public License Version
2 or
later (the
"GPL"), or
28 # the GNU Lesser General Public License Version
2.1 or
later (the
"LGPL"),
29 # in which
case the provisions
of the GPL or the LGPL are applicable instead
30 # of those above
. If you wish to allow
use of your version
of this file only
31 # under the terms
of either the GPL or the LGPL
, and not to allow others to
32 # use your version
of this file under the terms
of the MPL
, indicate your
33 # decision by deleting the provisions above and replace them
with the notice
34 # and other provisions required by the GPL or the LGPL
. If you
do not
delete
35 # the provisions above
, a recipient may
use your version
of this file under
36 # the terms
of any one
of the MPL
, the GPL or the LGPL
.
38 # ***** END LICENSE BLOCK
***** */
40 const Cc
= Components
.classes
;
41 const Ci
= Components
.interfaces
;
42 const Cr
= Components
.results
;
45 dump("*** " + str
+ "\n");
48 const FC_CLASSID
= Components
.ID("{229fa115-9412-4d32-baf3-2fc407f76fb1}");
49 const FC_CLASSNAME
= "Feed Stream Converter";
50 const FS_CLASSID
= Components
.ID("{2376201c-bbc6-472f-9b62-7548040a61c6}");
51 const FS_CLASSNAME
= "Feed Result Service";
52 const FS_CONTRACTID
= "@mozilla.org/browser/feeds/result-service;1";
53 const FPH_CONTRACTID
= "@mozilla.org/network/protocol;1?name=feed";
54 const FPH_CLASSID
= Components
.ID("{4f91ef2e-57ba-472e-ab7a-b4999e42d6c0}");
55 const FPH_CLASSNAME
= "Feed Protocol Handler";
56 const PCPH_CONTRACTID
= "@mozilla.org/network/protocol;1?name=pcast";
57 const PCPH_CLASSID
= Components
.ID("{1c31ed79-accd-4b94-b517-06e0c81999d5}");
58 const PCPH_CLASSNAME
= "Podcast Protocol Handler";
60 const TYPE_MAYBE_FEED
= "application/vnd.mozilla.maybe.feed";
61 const TYPE_MAYBE_VIDEO_FEED
= "application/vnd.mozilla.maybe.video.feed";
62 const TYPE_MAYBE_AUDIO_FEED
= "application/vnd.mozilla.maybe.audio.feed";
63 const TYPE_ANY
= "*/*";
65 const FEEDHANDLER_URI
= "about:feeds";
67 const PREF_SELECTED_APP
= "browser.feeds.handlers.application";
68 const PREF_SELECTED_WEB
= "browser.feeds.handlers.webservice";
69 const PREF_SELECTED_ACTION
= "browser.feeds.handler";
70 const PREF_SELECTED_READER
= "browser.feeds.handler.default";
72 const PREF_VIDEO_SELECTED_APP
= "browser.videoFeeds.handlers.application";
73 const PREF_VIDEO_SELECTED_WEB
= "browser.videoFeeds.handlers.webservice";
74 const PREF_VIDEO_SELECTED_ACTION
= "browser.videoFeeds.handler";
75 const PREF_VIDEO_SELECTED_READER
= "browser.videoFeeds.handler.default";
77 const PREF_AUDIO_SELECTED_APP
= "browser.audioFeeds.handlers.application";
78 const PREF_AUDIO_SELECTED_WEB
= "browser.audioFeeds.handlers.webservice";
79 const PREF_AUDIO_SELECTED_ACTION
= "browser.audioFeeds.handler";
80 const PREF_AUDIO_SELECTED_READER
= "browser.audioFeeds.handler.default";
82 function getPrefAppForType(t
) {
84 case Ci
.nsIFeed
.TYPE_VIDEO
:
85 return PREF_VIDEO_SELECTED_APP
;
87 case Ci
.nsIFeed
.TYPE_AUDIO
:
88 return PREF_AUDIO_SELECTED_APP
;
91 return PREF_SELECTED_APP
;
95 function getPrefWebForType(t
) {
97 case Ci
.nsIFeed
.TYPE_VIDEO
:
98 return PREF_VIDEO_SELECTED_WEB
;
100 case Ci
.nsIFeed
.TYPE_AUDIO
:
101 return PREF_AUDIO_SELECTED_WEB
;
104 return PREF_SELECTED_WEB
;
108 function getPrefActionForType(t
) {
110 case Ci
.nsIFeed
.TYPE_VIDEO
:
111 return PREF_VIDEO_SELECTED_ACTION
;
113 case Ci
.nsIFeed
.TYPE_AUDIO
:
114 return PREF_AUDIO_SELECTED_ACTION
;
117 return PREF_SELECTED_ACTION
;
121 function getPrefReaderForType(t
) {
123 case Ci
.nsIFeed
.TYPE_VIDEO
:
124 return PREF_VIDEO_SELECTED_READER
;
126 case Ci
.nsIFeed
.TYPE_AUDIO
:
127 return PREF_AUDIO_SELECTED_READER
;
130 return PREF_SELECTED_READER
;
134 function safeGetCharPref(pref
, defaultValue
) {
136 Cc
["@mozilla.org/preferences-service;1"].
137 getService(Ci
.nsIPrefBranch
);
139 return prefs
.getCharPref(pref
);
146 function FeedConverter() {
148 FeedConverter
.prototype = {
150 * This is the downloaded text data for the feed.
155 * This is the object listening to the conversion, which is ultimately the
156 * docshell for the load.
161 * Records if the feed was sniffed
166 * See nsIStreamConverter.idl
168 canConvert
: function FC_canConvert(sourceType
, destinationType
) {
169 // We only support one conversion.
170 return destinationType
== TYPE_ANY
&& ((sourceType
== TYPE_MAYBE_FEED
) ||
171 (sourceType
== TYPE_MAYBE_VIDEO
) ||
172 (sourceType
== TYPE_MAYBE_AUDIO
));
176 * See nsIStreamConverter.idl
178 convert
: function FC_convert(sourceStream
, sourceType
, destinationType
,
180 throw Cr
.NS_ERROR_NOT_IMPLEMENTED
;
184 * See nsIStreamConverter.idl
186 asyncConvertData
: function FC_asyncConvertData(sourceType
, destinationType
,
188 this._listener
= listener
;
192 * Whether or not the preview page is being forced.
194 _forcePreviewPage
: false,
197 * Release our references to various things once we're done using them.
199 _releaseHandles
: function FC__releaseHandles() {
200 this._listener
= null;
201 this._request
= null;
202 this._processor
= null;
206 * See nsIFeedResultListener.idl
208 handleResult
: function FC_handleResult(result
) {
209 // Feeds come in various content types, which our feed sniffer coerces to
210 // the maybe.feed type. However, feeds are used as a transport for
211 // different data types, e.g. news/blogs (traditional feed), video/audio
212 // (podcasts) and photos (photocasts, photostreams). Each of these is
213 // different in that there's a different class of application suitable for
214 // handling feeds of that type, but without a content-type differentiation
215 // it is difficult for us to disambiguate.
217 // The other problem is that if the user specifies an auto-action handler
218 // for one feed application, the fact that the content type is shared means
219 // that all other applications will auto-load with that handler too,
220 // regardless of the content-type.
222 // This means that content-type alone is not enough to determine whether
223 // or not a feed should be auto-handled. This means that for feeds we need
224 // to always use this stream converter, even when an auto-action is
225 // specified, not the basic one provided by WebContentConverter. This
226 // converter needs to consume all of the data and parse it, and based on
227 // that determination make a judgement about type.
229 // Since there are no content types for this content, and I'm not going to
230 // invent any, the upshot is that while a user can set an auto-handler for
231 // generic feed content, the system will prevent them from setting an auto-
232 // handler for other stream types. In those cases, the user will always see
233 // the preview page and have to select a handler. We can guess and show
234 // a client handler, but will not be able to show web handlers for those
237 // If this is just a feed, not some kind of specialized application, then
238 // auto-handlers can be set and we should obey them.
241 Cc
["@mozilla.org/browser/feeds/result-service;1"].
242 getService(Ci
.nsIFeedResultService
);
243 if (!this._forcePreviewPage
&& result
.doc
) {
244 var feed
= result
.doc
.QueryInterface(Ci
.nsIFeed
);
245 var handler
= safeGetCharPref(getPrefActionForType(feed
.type
), "ask");
247 if (handler
!= "ask") {
248 if (handler
== "reader")
249 handler
= safeGetCharPref(getPrefReaderForType(feed
.type
), "bookmarks");
253 Cc
["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
254 getService(Ci
.nsIWebContentConverterService
);
255 if ((feed
.type
== Ci
.nsIFeed
.TYPE_FEED
&&
256 wccr
.getAutoHandler(TYPE_MAYBE_FEED
)) ||
257 (feed
.type
== Ci
.nsIFeed
.TYPE_VIDEO
&&
258 wccr
.getAutoHandler(TYPE_MAYBE_VIDEO_FEED
)) ||
259 (feed
.type
== Ci
.nsIFeed
.TYPE_AUDIO
&&
260 wccr
.getAutoHandler(TYPE_MAYBE_AUDIO_FEED
))) {
261 wccr
.loadPreferredHandler(this._request
);
267 LOG("unexpected handler: " + handler
);
268 // fall through -- let feed service handle error
272 var title
= feed
.title
? feed
.title
.plainText() : "";
273 var desc
= feed
.subtitle
? feed
.subtitle
.plainText() : "";
274 feedService
.addToClientReader(result
.uri
.spec
, title
, desc
, feed
.type
);
276 } catch(ex
) { /* fallback to preview mode */ }
282 Cc
["@mozilla.org/network/io-service;1"].
283 getService(Ci
.nsIIOService
);
286 // show the feed page if it wasn't sniffed and we have a document,
287 // or we have a document, title, and link or id
288 if (result
.doc
&& (!this._sniffed
||
289 (result
.doc
.title
&& (result
.doc
.link
|| result
.doc
.id
)))) {
291 // If there was no automatic handler, or this was a podcast,
292 // photostream or some other kind of application, we must always
293 // show the preview page.
295 // Store the result in the result service so that the display
296 // page can access it.
298 feedService
.addFeedResult(result
);
300 // Now load the actual XUL document.
301 var chromeURI
= ios
.newURI(FEEDHANDLER_URI
, null, null);
302 chromeChannel
= ios
.newChannelFromURI(chromeURI
, null);
303 chromeChannel
.originalURI
= result
.uri
;
306 chromeChannel
= ios
.newChannelFromURI(result
.uri
, null);
308 chromeChannel
.loadGroup
= this._request
.loadGroup
;
309 chromeChannel
.asyncOpen(this._listener
, null);
312 this._releaseHandles();
317 * See nsIStreamListener.idl
319 onDataAvailable
: function FC_onDataAvailable(request
, context
, inputStream
,
320 sourceOffset
, count
) {
322 this._processor
.onDataAvailable(request
, context
, inputStream
,
323 sourceOffset
, count
);
327 * See nsIRequestObserver.idl
329 onStartRequest
: function FC_onStartRequest(request
, context
) {
330 var channel
= request
.QueryInterface(Ci
.nsIChannel
);
332 // Check for a header that tells us there was no sniffing
333 // The value doesn't matter.
335 var httpChannel
= channel
.QueryInterface(Ci
.nsIHttpChannel
);
336 var noSniff
= httpChannel
.getResponseHeader("X-Moz-Is-Feed");
339 this._sniffed
= true;
342 this._request
= request
;
344 // Save and reset the forced state bit early, in case there's some kind of
347 Cc
["@mozilla.org/browser/feeds/result-service;1"].
348 getService(Ci
.nsIFeedResultService
);
349 this._forcePreviewPage
= feedService
.forcePreviewPage
;
350 feedService
.forcePreviewPage
= false;
352 // Parse feed data as it comes in
354 Cc
["@mozilla.org/feed-processor;1"].
355 createInstance(Ci
.nsIFeedProcessor
);
356 this._processor
.listener
= this;
357 this._processor
.parseAsync(null, channel
.URI
);
359 this._processor
.onStartRequest(request
, context
);
363 * See nsIRequestObserver.idl
365 onStopRequest
: function FC_onStopReqeust(request
, context
, status
) {
367 this._processor
.onStopRequest(request
, context
, status
);
371 * See nsISupports.idl
373 QueryInterface
: function FC_QueryInterface(iid
) {
374 if (iid
.equals(Ci
.nsIFeedResultListener
) ||
375 iid
.equals(Ci
.nsIStreamConverter
) ||
376 iid
.equals(Ci
.nsIStreamListener
) ||
377 iid
.equals(Ci
.nsIRequestObserver
)||
378 iid
.equals(Ci
.nsISupports
))
380 throw Cr
.NS_ERROR_NO_INTERFACE
;
384 var FeedConverterFactory
= {
385 createInstance
: function FS_createInstance(outer
, iid
) {
387 throw Cr
.NS_ERROR_NO_AGGREGATION
;
388 return new FeedConverter().QueryInterface(iid
);
391 QueryInterface
: function FS_QueryInterface(iid
) {
392 if (iid
.equals(Ci
.nsIFactory
) ||
393 iid
.equals(Ci
.nsISupports
))
395 throw Cr
.NS_ERROR_NO_INTERFACE
;
400 * Keeps parsed FeedResults around for use elsewhere in the UI after the stream
401 * converter completes.
403 var FeedResultService
= {
406 * A URI spec -> [nsIFeedResult] hash. We have to keep a list as the
407 * value in case the same URI is requested concurrently.
412 * See nsIFeedResultService.idl
414 forcePreviewPage
: false,
417 * See nsIFeedResultService.idl
419 addToClientReader
: function FRS_addToClientReader(spec
, title
, subtitle
, feedType
) {
421 Cc
["@mozilla.org/preferences-service;1"].
422 getService(Ci
.nsIPrefBranch
);
424 var handler
= safeGetCharPref(getPrefActionForType(feedType
), "bookmarks");
425 if (handler
== "ask" || handler
== "reader")
426 handler
= safeGetCharPref(getPrefReaderForType(feedType
), "bookmarks");
430 var clientApp
= prefs
.getComplexValue(getPrefAppForType(feedType
), Ci
.nsILocalFile
);
432 // For the benefit of applications that might know how to deal with more
433 // URLs than just feeds, send feed: URLs in the following format:
435 // http urls: replace scheme with feed, e.g.
436 // http://foo.com/index.rdf -> feed://foo.com/index.rdf
437 // other urls: prepend feed: scheme, e.g.
438 // https://foo.com/index.rdf -> feed:https://foo.com/index.rdf
440 Cc
["@mozilla.org/network/io-service;1"].
441 getService(Ci
.nsIIOService
);
442 var feedURI
= ios
.newURI(spec
, null, null);
443 if (feedURI
.schemeIs("http")) {
444 feedURI
.scheme
= "feed";
448 spec
= "feed:" + spec
;
450 // Retrieving the shell service might fail on some systems, most
451 // notably systems where GNOME is not installed.
454 Cc
["@mozilla.org/browser/shell-service;1"].
455 getService(Ci
.nsIShellService
);
456 ss
.openApplicationWithURI(clientApp
, spec
);
458 // If we couldn't use the shell service, fallback to using a
459 // nsIProcess instance
461 Cc
["@mozilla.org/process/util;1"].
462 createInstance(Ci
.nsIProcess
);
464 p
.run(false, [spec
], 1);
469 // "web" should have been handled elsewhere
470 LOG("unexpected handler: " + handler
);
474 Cc
["@mozilla.org/appshell/window-mediator;1"].
475 getService(Ci
.nsIWindowMediator
);
476 var topWindow
= wm
.getMostRecentWindow("navigator:browser");
477 topWindow
.PlacesCommandHook
.addLiveBookmark(spec
, title
, subtitle
);
483 * See nsIFeedResultService.idl
485 addFeedResult
: function FRS_addFeedResult(feedResult
) {
486 NS_ASSERT(feedResult
.uri
!= null, "null URI!");
487 NS_ASSERT(feedResult
.uri
!= null, "null feedResult!");
488 var spec
= feedResult
.uri
.spec
;
489 if(!this._results
[spec
])
490 this._results
[spec
] = [];
491 this._results
[spec
].push(feedResult
);
495 * See nsIFeedResultService.idl
497 getFeedResult
: function RFS_getFeedResult(uri
) {
498 NS_ASSERT(uri
!= null, "null URI!");
499 var resultList
= this._results
[uri
.spec
];
500 for (var i
in resultList
) {
501 if (resultList
[i
].uri
== uri
)
502 return resultList
[i
];
508 * See nsIFeedResultService.idl
510 removeFeedResult
: function FRS_removeFeedResult(uri
) {
511 NS_ASSERT(uri
!= null, "null URI!");
512 var resultList
= this._results
[uri
.spec
];
516 for (var i
= 0; i
< resultList
.length
; ++i
) {
517 if (resultList
[i
].uri
== uri
) {
518 delete resultList
[i
];
523 // send the holes to the end
526 resultList
.splice(resultList
.length
- deletions
, deletions
);
527 if (resultList
.length
== 0)
528 delete this._results
[uri
.spec
];
531 createInstance
: function FRS_createInstance(outer
, iid
) {
533 throw Cr
.NS_ERROR_NO_AGGREGATION
;
534 return this.QueryInterface(iid
);
537 QueryInterface
: function FRS_QueryInterface(iid
) {
538 if (iid
.equals(Ci
.nsIFeedResultService
) ||
539 iid
.equals(Ci
.nsIFactory
) ||
540 iid
.equals(Ci
.nsISupports
))
542 throw Cr
.NS_ERROR_NOT_IMPLEMENTED
;
547 * A protocol handler that attempts to deal with the variant forms of feed:
548 * URIs that are actually either http or https.
550 function FeedProtocolHandler(scheme
) {
551 this._scheme
= scheme
;
553 Cc
["@mozilla.org/network/io-service;1"].
554 getService(Ci
.nsIIOService
);
555 this._http
= ios
.getProtocolHandler("http");
557 FeedProtocolHandler
.prototype = {
563 get protocolFlags() {
564 return this._http
.protocolFlags
;
568 return this._http
.defaultPort
;
571 allowPort
: function FPH_allowPort(port
, scheme
) {
572 return this._http
.allowPort(port
, scheme
);
575 newURI
: function FPH_newURI(spec
, originalCharset
, baseURI
) {
576 // See bug 408599 - feed URIs can be either standard URLs of the form
577 // feed://example.com, in which case the real protocol is http, or nested
578 // URIs of the form feed:realscheme:. When realscheme is either http or
579 // https, we deal with the way that creates a standard URL with the
580 // realscheme as the host by unmangling in newChannel; for others, we fail
581 // rather than let it wind up loading something like www.realscheme.com//foo
583 const feedSlashes
= "feed://";
584 const feedHttpSlashes
= "feed:http://";
585 const feedHttpsSlashes
= "feed:https://";
586 const NS_ERROR_MALFORMED_URI
= 0x804B000A;
588 if (spec
.substr(0, feedSlashes
.length
) != feedSlashes
&&
589 spec
.substr(0, feedHttpSlashes
.length
) != feedHttpSlashes
&&
590 spec
.substr(0, feedHttpsSlashes
.length
) != feedHttpsSlashes
)
591 throw NS_ERROR_MALFORMED_URI
;
594 Cc
["@mozilla.org/network/standard-url;1"].
595 createInstance(Ci
.nsIStandardURL
);
596 uri
.init(Ci
.nsIStandardURL
.URLTYPE_STANDARD
, 80, spec
, originalCharset
,
601 newChannel
: function FPH_newChannel(aUri
) {
603 Cc
["@mozilla.org/network/io-service;1"].
604 getService(Ci
.nsIIOService
);
605 // feed: URIs either start feed://, in which case the real scheme is http:
606 // or feed:http(s)://, (which by now we've changed to feed://realscheme//)
607 var feedSpec
= aUri
.spec
;
608 const httpsChunk
= "feed://https//";
609 const httpChunk
= "feed://http//";
610 if (feedSpec
.substr(0, httpsChunk
.length
) == httpsChunk
)
611 feedSpec
= "https://" + feedSpec
.substr(httpsChunk
.length
);
612 else if (feedSpec
.substr(0, httpChunk
.length
) == httpChunk
)
613 feedSpec
= "http://" + feedSpec
.substr(httpChunk
.length
);
615 feedSpec
= feedSpec
.replace(/^feed/, "http");
617 var uri
= ios
.newURI(feedSpec
, aUri
.originCharset
, null);
619 ios
.newChannelFromURI(uri
, null).QueryInterface(Ci
.nsIHttpChannel
);
620 // Set this so we know this is supposed to be a feed
621 channel
.setRequestHeader("X-Moz-Is-Feed", "1", false);
622 channel
.originalURI
= aUri
;
626 QueryInterface
: function FPH_QueryInterface(iid
) {
627 if (iid
.equals(Ci
.nsIProtocolHandler
) ||
628 iid
.equals(Ci
.nsISupports
))
630 throw Cr
.NS_ERROR_NO_INTERFACE
;
635 QueryInterface
: function M_QueryInterface(iid
) {
636 if (iid
.equals(Ci
.nsIModule
) ||
637 iid
.equals(Ci
.nsISupports
))
639 throw Cr
.NS_ERROR_NO_INTERFACE
;
642 getClassObject
: function M_getClassObject(cm
, cid
, iid
) {
643 if (!iid
.equals(Ci
.nsIFactory
))
644 throw Cr
.NS_ERROR_NOT_IMPLEMENTED
;
646 if (cid
.equals(FS_CLASSID
))
647 return FeedResultService
;
648 if (cid
.equals(FPH_CLASSID
))
649 return new GenericComponentFactory(FeedProtocolHandler
, "feed");
650 if (cid
.equals(PCPH_CLASSID
))
651 return new GenericComponentFactory(FeedProtocolHandler
, "pcast");
652 if (cid
.equals(FC_CLASSID
))
653 return new GenericComponentFactory(FeedConverter
);
655 throw Cr
.NS_ERROR_NO_INTERFACE
;
658 registerSelf
: function M_registerSelf(cm
, file
, location
, type
) {
659 var cr
= cm
.QueryInterface(Ci
.nsIComponentRegistrar
);
661 cr
.registerFactoryLocation(FS_CLASSID
, FS_CLASSNAME
, FS_CONTRACTID
,
662 file
, location
, type
);
663 cr
.registerFactoryLocation(FPH_CLASSID
, FPH_CLASSNAME
, FPH_CONTRACTID
,
664 file
, location
, type
);
665 cr
.registerFactoryLocation(PCPH_CLASSID
, PCPH_CLASSNAME
, PCPH_CONTRACTID
,
666 file
, location
, type
);
668 // The feed converter is always attached, since parsing must be done to
669 // determine whether or not auto-handling can occur.
670 const converterPrefix
= "@mozilla.org/streamconv;1?from=";
671 var converterContractID
=
672 converterPrefix
+ TYPE_MAYBE_FEED
+ "&to=" + TYPE_ANY
;
673 cr
.registerFactoryLocation(FC_CLASSID
, FC_CLASSNAME
, converterContractID
,
674 file
, location
, type
);
676 converterContractID
=
677 converterPrefix
+ TYPE_MAYBE_VIDEO_FEED
+ "&to=" + TYPE_ANY
;
678 cr
.registerFactoryLocation(FC_CLASSID
, FC_CLASSNAME
, converterContractID
,
679 file
, location
, type
);
681 converterContractID
=
682 converterPrefix
+ TYPE_MAYBE_AUDIO_FEED
+ "&to=" + TYPE_ANY
;
683 cr
.registerFactoryLocation(FC_CLASSID
, FC_CLASSNAME
, converterContractID
,
684 file
, location
, type
);
687 unregisterSelf
: function M_unregisterSelf(cm
, location
, type
) {
688 var cr
= cm
.QueryInterface(Ci
.nsIComponentRegistrar
);
689 cr
.unregisterFactoryLocation(FPH_CLASSID
, location
);
690 cr
.unregisterFactoryLocation(PCPH_CLASSID
, location
);
693 canUnload
: function M_canUnload(cm
) {
698 function NSGetModule(cm
, file
) {
702 #include
../../../../toolkit
/content/debug.js
703 #include GenericFactory
.js