Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / browser / components / feeds / src / WebContentConverter.js
blob05ec4a38b620e7b2239d37ca0d6d8f20b66e5a7c
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
13 # License.
15 # The Original Code is the Web Content Converter System.
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.
21 # Contributor(s):
22 # Ben Goodger <beng@google.com>
23 # Asaf Romano <mano@mozilla.com>
24 # Dan Mosedale <dmose@mozilla.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 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
42 const Cc = Components.classes;
43 const Ci = Components.interfaces;
44 const Cr = Components.results;
46 function LOG(str) {
47 dump("*** " + str + "\n");
50 const WCCR_CONTRACTID = "@mozilla.org/embeddor.implemented/web-content-handler-registrar;1";
51 const WCCR_CLASSID = Components.ID("{792a7e82-06a0-437c-af63-b2d12e808acc}");
52 const WCCR_CLASSNAME = "Web Content Handler Registrar";
54 const WCC_CLASSID = Components.ID("{db7ebf28-cc40-415f-8a51-1b111851df1e}");
55 const WCC_CLASSNAME = "Web Service Handler";
57 const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
58 const TYPE_ANY = "*/*";
60 const PREF_CONTENTHANDLERS_AUTO = "browser.contentHandlers.auto.";
61 const PREF_CONTENTHANDLERS_BRANCH = "browser.contentHandlers.types.";
62 const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
63 const PREF_SELECTED_ACTION = "browser.feeds.handler";
64 const PREF_SELECTED_READER = "browser.feeds.handler.default";
65 const PREF_HANDLER_EXTERNAL_PREFIX = "network.protocol-handler.external";
66 const PREF_ALLOW_DIFFERENT_HOST = "gecko.handlerService.allowRegisterFromDifferentHost";
68 const STRING_BUNDLE_URI = "chrome://browser/locale/feeds/subscribe.properties";
70 const NS_ERROR_MODULE_DOM = 2152923136;
71 const NS_ERROR_DOM_SYNTAX_ERR = NS_ERROR_MODULE_DOM + 12;
73 function WebContentConverter() {
75 WebContentConverter.prototype = {
76 convert: function WCC_convert() { },
77 asyncConvertData: function WCC_asyncConvertData() { },
78 onDataAvailable: function WCC_onDataAvailable() { },
79 onStopRequest: function WCC_onStopRequest() { },
81 onStartRequest: function WCC_onStartRequest(request, context) {
82 var wccr =
83 Cc[WCCR_CONTRACTID].
84 getService(Ci.nsIWebContentConverterService);
85 wccr.loadPreferredHandler(request);
88 QueryInterface: function WCC_QueryInterface(iid) {
89 if (iid.equals(Ci.nsIStreamConverter) ||
90 iid.equals(Ci.nsIStreamListener) ||
91 iid.equals(Ci.nsISupports))
92 return this;
93 throw Cr.NS_ERROR_NO_INTERFACE;
97 var WebContentConverterFactory = {
98 createInstance: function WCCF_createInstance(outer, iid) {
99 if (outer != null)
100 throw Cr.NS_ERROR_NO_AGGREGATION;
101 return new WebContentConverter().QueryInterface(iid);
104 QueryInterface: function WCC_QueryInterface(iid) {
105 if (iid.equals(Ci.nsIFactory) ||
106 iid.equals(Ci.nsISupports))
107 return this;
108 throw Cr.NS_ERROR_NO_INTERFACE;
112 function ServiceInfo(contentType, uri, name) {
113 this._contentType = contentType;
114 this._uri = uri;
115 this._name = name;
117 ServiceInfo.prototype = {
119 * See nsIHandlerApp
121 get name() {
122 return this._name;
126 * See nsIHandlerApp
128 equals: function SI_equals(aHandlerApp) {
129 if (!aHandlerApp)
130 throw Cr.NS_ERROR_NULL_POINTER;
132 if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo &&
133 aHandlerApp.contentType == this.contentType &&
134 aHandlerApp.uri == this.uri)
135 return true;
137 return false;
141 * See nsIWebContentHandlerInfo
143 get contentType() {
144 return this._contentType;
148 * See nsIWebContentHandlerInfo
150 get uri() {
151 return this._uri;
155 * See nsIWebContentHandlerInfo
157 getHandlerURI: function SI_getHandlerURI(uri) {
158 return this._uri.replace(/%s/gi, encodeURIComponent(uri));
161 QueryInterface: function SI_QueryInterface(iid) {
162 if (iid.equals(Ci.nsIWebContentHandlerInfo) ||
163 iid.equals(Ci.nsISupports))
164 return this;
165 throw Cr.NS_ERROR_NO_INTERFACE;
169 function WebContentConverterRegistrar() {
170 this._contentTypes = { };
171 this._autoHandleContentTypes = { };
174 WebContentConverterRegistrar.prototype = {
175 get stringBundle() {
176 var sb = Cc["@mozilla.org/intl/stringbundle;1"].
177 getService(Ci.nsIStringBundleService).
178 createBundle(STRING_BUNDLE_URI);
179 delete WebContentConverterRegistrar.prototype.stringBundle;
180 return WebContentConverterRegistrar.prototype.stringBundle = sb;
183 _getFormattedString: function WCCR__getFormattedString(key, params) {
184 return this.stringBundle.formatStringFromName(key, params, params.length);
187 _getString: function WCCR_getString(key) {
188 return this.stringBundle.GetStringFromName(key);
192 * See nsIWebContentConverterService
194 getAutoHandler:
195 function WCCR_getAutoHandler(contentType) {
196 contentType = this._resolveContentType(contentType);
197 if (contentType in this._autoHandleContentTypes)
198 return this._autoHandleContentTypes[contentType];
199 return null;
203 * See nsIWebContentConverterService
205 setAutoHandler:
206 function WCCR_setAutoHandler(contentType, handler) {
207 if (handler && !this._typeIsRegistered(contentType, handler.uri))
208 throw Cr.NS_ERROR_NOT_AVAILABLE;
210 contentType = this._resolveContentType(contentType);
211 this._setAutoHandler(contentType, handler);
213 var ps =
214 Cc["@mozilla.org/preferences-service;1"].
215 getService(Ci.nsIPrefService);
216 var autoBranch = ps.getBranch(PREF_CONTENTHANDLERS_AUTO);
217 if (handler)
218 autoBranch.setCharPref(contentType, handler.uri);
219 else if (autoBranch.prefHasUserValue(contentType))
220 autoBranch.clearUserPref(contentType);
222 ps.savePrefFile(null);
226 * Update the internal data structure (not persistent)
228 _setAutoHandler:
229 function WCCR__setAutoHandler(contentType, handler) {
230 if (handler)
231 this._autoHandleContentTypes[contentType] = handler;
232 else if (contentType in this._autoHandleContentTypes)
233 delete this._autoHandleContentTypes[contentType];
237 * See nsIWebContentConverterService
239 getWebContentHandlerByURI:
240 function WCCR_getWebContentHandlerByURI(contentType, uri) {
241 var handlers = this.getContentHandlers(contentType, { });
242 for (var i = 0; i < handlers.length; ++i) {
243 if (handlers[i].uri == uri)
244 return handlers[i];
246 return null;
250 * See nsIWebContentConverterService
252 loadPreferredHandler:
253 function WCCR_loadPreferredHandler(request) {
254 var channel = request.QueryInterface(Ci.nsIChannel);
255 var contentType = this._resolveContentType(channel.contentType);
256 var handler = this.getAutoHandler(contentType);
257 if (handler) {
258 request.cancel(Cr.NS_ERROR_FAILURE);
260 var webNavigation =
261 channel.notificationCallbacks.getInterface(Ci.nsIWebNavigation);
262 webNavigation.loadURI(handler.getHandlerURI(channel.URI.spec),
263 Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
264 null, null, null);
269 * See nsIWebContentConverterService
271 removeProtocolHandler:
272 function WCCR_removeProtocolHandler(aProtocol, aURITemplate) {
273 var eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
274 getService(Ci.nsIExternalProtocolService);
275 var handlerInfo = eps.getProtocolHandlerInfo(aProtocol);
276 var handlers = handlerInfo.possibleApplicationHandlers;
277 for (let i = 0; i < handlers.length; i++) {
278 try { // We only want to test web handlers
279 let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp);
280 if (handler.uriTemplate == aURITemplate) {
281 handlers.removeElementAt(i);
282 var hs = Cc["@mozilla.org/uriloader/handler-service;1"].
283 getService(Ci.nsIHandlerService);
284 hs.store(handlerInfo);
285 return;
287 } catch (e) { /* it wasn't a web handler */ }
292 * See nsIWebContentConverterService
294 removeContentHandler:
295 function WCCR_removeContentHandler(contentType, uri) {
296 function notURI(serviceInfo) {
297 return serviceInfo.uri != uri;
300 if (contentType in this._contentTypes) {
301 this._contentTypes[contentType] =
302 this._contentTypes[contentType].filter(notURI);
309 _mappings: {
310 "application/rss+xml": TYPE_MAYBE_FEED,
311 "application/atom+xml": TYPE_MAYBE_FEED,
315 * These are types for which there is a separate content converter aside
316 * from our built in generic one. We should not automatically register
317 * a factory for creating a converter for these types.
319 _blockedTypes: {
320 "application/vnd.mozilla.maybe.feed": true,
324 * Determines the "internal" content type based on the _mappings.
325 * @param contentType
326 * @returns The resolved contentType value.
328 _resolveContentType:
329 function WCCR__resolveContentType(contentType) {
330 if (contentType in this._mappings)
331 return this._mappings[contentType];
332 return contentType;
335 _makeURI: function(aURL, aOriginCharset, aBaseURI) {
336 var ioService = Components.classes["@mozilla.org/network/io-service;1"]
337 .getService(Components.interfaces.nsIIOService);
338 return ioService.newURI(aURL, aOriginCharset, aBaseURI);
341 _checkAndGetURI:
342 function WCCR_checkAndGetURI(aURIString, aContentWindow)
344 try {
345 var uri = this._makeURI(aURIString);
346 } catch (ex) {
347 // not supposed to throw according to spec
348 return;
351 // For security reasons we reject non-http(s) urls (see bug 354316),
352 // we may need to revise this once we support more content types
353 // XXX this should be a "security exception" according to spec, but that
354 // isn't defined yet.
355 if (uri.scheme != "http" && uri.scheme != "https")
356 throw("Permission denied to add " + uri.spec + " as a content or protocol handler");
358 // We also reject handlers registered from a different host (see bug 402287)
359 // The pref allows us to test the feature
360 var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
361 if ((!pb.prefHasUserValue(PREF_ALLOW_DIFFERENT_HOST) ||
362 !pb.getBoolPref(PREF_ALLOW_DIFFERENT_HOST)) &&
363 aContentWindow.location.hostname != uri.host)
364 throw("Permission denied to add " + uri.spec + " as a content or protocol handler");
366 // If the uri doesn't contain '%s', it won't be a good handler
367 if (uri.spec.indexOf("%s") < 0)
368 throw NS_ERROR_DOM_SYNTAX_ERR;
370 return uri;
374 * Determines if a web handler is already registered.
376 * @param aProtocol
377 * The scheme of the web handler we are checking for.
378 * @param aURITemplate
379 * The URI template that the handler uses to handle the protocol.
380 * @return true if it is already registered, false otherwise.
382 _protocolHandlerRegistered:
383 function WCCR_protocolHandlerRegistered(aProtocol, aURITemplate) {
384 var eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
385 getService(Ci.nsIExternalProtocolService);
386 var handlerInfo = eps.getProtocolHandlerInfo(aProtocol);
387 var handlers = handlerInfo.possibleApplicationHandlers;
388 for (let i = 0; i < handlers.length; i++) {
389 try { // We only want to test web handlers
390 let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp);
391 if (handler.uriTemplate == aURITemplate)
392 return true;
393 } catch (e) { /* it wasn't a web handler */ }
395 return false;
399 * See nsIWebContentHandlerRegistrar
401 registerProtocolHandler:
402 function WCCR_registerProtocolHandler(aProtocol, aURIString, aTitle, aContentWindow) {
403 LOG("registerProtocolHandler(" + aProtocol + "," + aURIString + "," + aTitle + ")");
405 // First, check to make sure this isn't already handled internally (we don't
406 // want to let them take over, say "chrome").
407 var ios = Cc["@mozilla.org/network/io-service;1"].
408 getService(Ci.nsIIOService);
409 var handler = ios.getProtocolHandler(aProtocol);
410 if (!(handler instanceof Ci.nsIExternalProtocolHandler)) {
411 // This is handled internally, so we don't want them to register
412 // XXX this should be a "security exception" according to spec, but that
413 // isn't defined yet.
414 throw("Permission denied to add " + aURIString + "as a protocol handler");
417 // check if it is in the black list
418 var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
419 var allowed;
420 try {
421 allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "." + aProtocol);
423 catch (e) {
424 allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "-default");
426 if (!allowed) {
427 // XXX this should be a "security exception" according to spec
428 throw("Not allowed to register a protocol handler for " + aProtocol);
431 var uri = this._checkAndGetURI(aURIString, aContentWindow);
433 var buttons, message;
434 if (this._protocolHandlerRegistered(aProtocol, uri.spec))
435 message = this._getFormattedString("protocolHandlerRegistered",
436 [aTitle, aProtocol]);
437 else {
438 // Now Ask the user and provide the proper callback
439 message = this._getFormattedString("addProtocolHandler",
440 [aTitle, uri.host, aProtocol]);
441 var fis = Cc["@mozilla.org/browser/favicon-service;1"].
442 getService(Ci.nsIFaviconService);
443 var notificationIcon = fis.getFaviconLinkForIcon(uri);
444 var notificationValue = "Protocol Registration: " + aProtocol;
445 var addButton = {
446 label: this._getString("addProtocolHandlerAddButton"),
447 accessKey: this._getString("addHandlerAddButtonAccesskey"),
448 protocolInfo: { protocol: aProtocol, uri: uri.spec, name: aTitle },
450 callback:
451 function WCCR_addProtocolHandlerButtonCallback(aNotification, aButtonInfo) {
452 var protocol = aButtonInfo.protocolInfo.protocol;
453 var uri = aButtonInfo.protocolInfo.uri;
454 var name = aButtonInfo.protocolInfo.name;
456 var handler = Cc["@mozilla.org/uriloader/web-handler-app;1"].
457 createInstance(Ci.nsIWebHandlerApp);
458 handler.name = name;
459 handler.uriTemplate = uri;
461 var eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"].
462 getService(Ci.nsIExternalProtocolService);
463 var handlerInfo = eps.getProtocolHandlerInfo(protocol);
464 handlerInfo.possibleApplicationHandlers.appendElement(handler, false);
466 // Since the user has agreed to add a new handler, chances are good
467 // that the next time they see a handler of this type, they're going
468 // to want to use it. Reset the handlerInfo to ask before the next
469 // use.
470 handlerInfo.alwaysAskBeforeHandling = true;
472 var hs = Cc["@mozilla.org/uriloader/handler-service;1"].
473 getService(Ci.nsIHandlerService);
474 hs.store(handlerInfo);
477 buttons = [addButton];
480 var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow);
481 var browserElement = this._getBrowserForContentWindow(browserWindow, aContentWindow);
482 var notificationBox = browserWindow.getBrowser().getNotificationBox(browserElement);
483 notificationBox.appendNotification(message,
484 notificationValue,
485 notificationIcon,
486 notificationBox.PRIORITY_INFO_LOW,
487 buttons);
491 * See nsIWebContentHandlerRegistrar
492 * If a DOM window is provided, then the request came from content, so we
493 * prompt the user to confirm the registration.
495 registerContentHandler:
496 function WCCR_registerContentHandler(aContentType, aURIString, aTitle, aContentWindow) {
497 LOG("registerContentHandler(" + aContentType + "," + aURIString + "," + aTitle + ")");
499 // We only support feed types at present.
500 // XXX this should be a "security exception" according to spec, but that
501 // isn't defined yet.
502 var contentType = this._resolveContentType(aContentType);
503 if (contentType != TYPE_MAYBE_FEED)
504 return;
506 if (aContentWindow) {
507 var uri = this._checkAndGetURI(aURIString, aContentWindow);
509 var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow);
510 var browserElement = this._getBrowserForContentWindow(browserWindow, aContentWindow);
511 var notificationBox = browserWindow.getBrowser().getNotificationBox(browserElement);
512 this._appendFeedReaderNotification(uri, aTitle, notificationBox);
514 else
515 this._registerContentHandler(contentType, aURIString, aTitle);
519 * Returns the browser chrome window in which the content window is in
521 _getBrowserWindowForContentWindow:
522 function WCCR__getBrowserWindowForContentWindow(aContentWindow) {
523 return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
524 .getInterface(Ci.nsIWebNavigation)
525 .QueryInterface(Ci.nsIDocShellTreeItem)
526 .rootTreeItem
527 .QueryInterface(Ci.nsIInterfaceRequestor)
528 .getInterface(Ci.nsIDOMWindow)
529 .wrappedJSObject;
533 * Returns the <xul:browser> element associated with the given content
534 * window.
536 * @param aBrowserWindow
537 * The browser window in which the content window is in.
538 * @param aContentWindow
539 * The content window. It's possible to pass a child content window
540 * (i.e. the content window of a frame/iframe).
542 _getBrowserForContentWindow:
543 function WCCR__getBrowserForContentWindow(aBrowserWindow, aContentWindow) {
544 // This depends on pseudo APIs of browser.js and tabbrowser.xml
545 aContentWindow = aContentWindow.top;
546 var browsers = aBrowserWindow.getBrowser().browsers;
547 for (var i = 0; i < browsers.length; ++i) {
548 if (browsers[i].contentWindow == aContentWindow)
549 return browsers[i];
554 * Appends a notifcation for the given feed reader details.
556 * The notification could be either a pseudo-dialog which lets
557 * the user to add the feed reader:
558 * [ [icon] Add %feed-reader-name% (%feed-reader-host%) as a Feed Reader? (Add) [x] ]
560 * or a simple message for the case where the feed reader is already registered:
561 * [ [icon] %feed-reader-name% is already registered as a Feed Reader [x] ]
563 * A new notification isn't appended if the given notificationbox has a
564 * notification for the same feed reader.
566 * @param aURI
567 * The url of the feed reader as a nsIURI object
568 * @param aName
569 * The feed reader name as it was passed to registerContentHandler
570 * @param aNotificationBox
571 * The notification box to which a notification might be appended
572 * @return true if a notification has been appended, false otherwise.
574 _appendFeedReaderNotification:
575 function WCCR__appendFeedReaderNotification(aURI, aName, aNotificationBox) {
576 var uriSpec = aURI.spec;
577 var notificationValue = "feed reader notification: " + uriSpec;
578 var notificationIcon = aURI.prePath + "/favicon.ico";
580 // Don't append a new notification if the notificationbox
581 // has a notification for the given feed reader already
582 if (aNotificationBox.getNotificationWithValue(notificationValue))
583 return false;
585 var buttons, message;
586 if (this.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uriSpec))
587 message = this._getFormattedString("handlerRegistered", [aName]);
588 else {
589 message = this._getFormattedString("addHandler", [aName, aURI.host]);
590 var self = this;
591 var addButton = {
592 _outer: self,
593 label: self._getString("addHandlerAddButton"),
594 accessKey: self._getString("addHandlerAddButtonAccesskey"),
595 feedReaderInfo: { uri: uriSpec, name: aName },
597 /* static */
598 callback:
599 function WCCR__addFeedReaderButtonCallback(aNotification, aButtonInfo) {
600 var uri = aButtonInfo.feedReaderInfo.uri;
601 var name = aButtonInfo.feedReaderInfo.name;
602 var outer = aButtonInfo._outer;
604 // The reader could have been added from another window mean while
605 if (!outer.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uri))
606 outer._registerContentHandler(TYPE_MAYBE_FEED, uri, name);
608 // avoid reference cycles
609 aButtonInfo._outer = null;
611 return false;
614 buttons = [addButton];
617 aNotificationBox.appendNotification(message,
618 notificationValue,
619 notificationIcon,
620 aNotificationBox.PRIORITY_INFO_LOW,
621 buttons);
622 return true;
626 * Save Web Content Handler metadata to persistent preferences.
627 * @param contentType
628 * The content Type being handled
629 * @param uri
630 * The uri of the web service
631 * @param title
632 * The human readable name of the web service
634 * This data is stored under:
636 * browser.contentHandlers.type0 = content/type
637 * browser.contentHandlers.uri0 = http://www.foo.com/q=%s
638 * browser.contentHandlers.title0 = Foo 2.0alphr
640 _saveContentHandlerToPrefs:
641 function WCCR__saveContentHandlerToPrefs(contentType, uri, title) {
642 var ps =
643 Cc["@mozilla.org/preferences-service;1"].
644 getService(Ci.nsIPrefService);
645 var i = 0;
646 var typeBranch = null;
647 while (true) {
648 typeBranch =
649 ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + i + ".");
650 try {
651 typeBranch.getCharPref("type");
652 ++i;
654 catch (e) {
655 // No more handlers
656 break;
659 if (typeBranch) {
660 typeBranch.setCharPref("type", contentType);
661 var pls =
662 Cc["@mozilla.org/pref-localizedstring;1"].
663 createInstance(Ci.nsIPrefLocalizedString);
664 pls.data = uri;
665 typeBranch.setComplexValue("uri", Ci.nsIPrefLocalizedString, pls);
666 pls.data = title;
667 typeBranch.setComplexValue("title", Ci.nsIPrefLocalizedString, pls);
669 ps.savePrefFile(null);
674 * Determines if there is a type with a particular uri registered for the
675 * specified content type already.
676 * @param contentType
677 * The content type that the uri handles
678 * @param uri
679 * The uri of the
681 _typeIsRegistered: function WCCR__typeIsRegistered(contentType, uri) {
682 if (!(contentType in this._contentTypes))
683 return false;
685 var services = this._contentTypes[contentType];
686 for (var i = 0; i < services.length; ++i) {
687 // This uri has already been registered
688 if (services[i].uri == uri)
689 return true;
691 return false;
695 * Gets a stream converter contract id for the specified content type.
696 * @param contentType
697 * The source content type for the conversion.
698 * @returns A contract id to construct a converter to convert between the
699 * contentType and *\/*.
701 _getConverterContractID: function WCCR__getConverterContractID(contentType) {
702 const template = "@mozilla.org/streamconv;1?from=%s&to=*/*";
703 return template.replace(/%s/, contentType);
707 * Register a web service handler for a content type.
709 * @param contentType
710 * the content type being handled
711 * @param uri
712 * the URI of the web service
713 * @param title
714 * the human readable name of the web service
716 _registerContentHandler:
717 function WCCR__registerContentHandler(contentType, uri, title) {
718 this._updateContentTypeHandlerMap(contentType, uri, title);
719 this._saveContentHandlerToPrefs(contentType, uri, title);
721 if (contentType == TYPE_MAYBE_FEED) {
722 // Make the new handler the last-selected reader in the preview page
723 // and make sure the preview page is shown the next time a feed is visited
724 var pb = Cc["@mozilla.org/preferences-service;1"].
725 getService(Ci.nsIPrefService).getBranch(null);
726 pb.setCharPref(PREF_SELECTED_READER, "web");
728 var supportsString =
729 Cc["@mozilla.org/supports-string;1"].
730 createInstance(Ci.nsISupportsString);
731 supportsString.data = uri;
732 pb.setComplexValue(PREF_SELECTED_WEB, Ci.nsISupportsString,
733 supportsString);
734 pb.setCharPref(PREF_SELECTED_ACTION, "ask");
735 this._setAutoHandler(TYPE_MAYBE_FEED, null);
740 * Update the content type -> handler map. This mapping is not persisted, use
741 * registerContentHandler or _saveContentHandlerToPrefs for that purpose.
742 * @param contentType
743 * The content Type being handled
744 * @param uri
745 * The uri of the web service
746 * @param title
747 * The human readable name of the web service
749 _updateContentTypeHandlerMap:
750 function WCCR__updateContentTypeHandlerMap(contentType, uri, title) {
751 if (!(contentType in this._contentTypes))
752 this._contentTypes[contentType] = [];
754 // Avoid adding duplicates
755 if (this._typeIsRegistered(contentType, uri))
756 return;
758 this._contentTypes[contentType].push(new ServiceInfo(contentType, uri, title));
760 if (!(contentType in this._blockedTypes)) {
761 var converterContractID = this._getConverterContractID(contentType);
762 var cr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
763 cr.registerFactory(WCC_CLASSID, WCC_CLASSNAME, converterContractID,
764 WebContentConverterFactory);
769 * See nsIWebContentConverterService
771 getContentHandlers:
772 function WCCR_getContentHandlers(contentType, countRef) {
773 countRef.value = 0;
774 if (!(contentType in this._contentTypes))
775 return [];
777 var handlers = this._contentTypes[contentType];
778 countRef.value = handlers.length;
779 return handlers;
783 * See nsIWebContentConverterService
785 resetHandlersForType:
786 function WCCR_resetHandlersForType(contentType) {
787 // currently unused within the tree, so only useful for extensions; previous
788 // impl. was buggy (and even infinite-looped!), so I argue that this is a
789 // definite improvement
790 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
794 * Registers a handler from the settings on a preferences branch.
796 * @param branch
797 * an nsIPrefBranch containing "type", "uri", and "title" preferences
798 * corresponding to the content handler to be registered
800 _registerContentHandlerWithBranch: function(branch) {
802 * Since we support up to six predefined readers, we need to handle gaps
803 * better, since the first branch with user-added values will be .6
805 * How we deal with that is to check to see if there's no prefs in the
806 * branch and stop cycling once that's true. This doesn't fix the case
807 * where a user manually removes a reader, but that's not supported yet!
809 var vals = branch.getChildList("", {});
810 if (vals.length == 0)
811 return;
813 try {
814 var type = branch.getCharPref("type");
815 var uri = branch.getComplexValue("uri", Ci.nsIPrefLocalizedString).data;
816 var title = branch.getComplexValue("title",
817 Ci.nsIPrefLocalizedString).data;
818 this._updateContentTypeHandlerMap(type, uri, title);
820 catch(ex) {
821 // do nothing, the next branch might have values
826 * Load the auto handler, content handler and protocol tables from
827 * preferences.
829 _init: function WCCR__init() {
830 var ps =
831 Cc["@mozilla.org/preferences-service;1"].
832 getService(Ci.nsIPrefService);
834 var kids = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH)
835 .getChildList("", {});
837 // first get the numbers of the providers by getting all ###.uri prefs
838 var nums = [];
839 for (var i = 0; i < kids.length; i++) {
840 var match = /^(\d+)\.uri$/.exec(kids[i]);
841 if (!match)
842 continue;
843 else
844 nums.push(match[1]);
847 // sort them, to get them back in order
848 nums.sort(function(a, b) {return a - b;});
850 // now register them
851 for (var i = 0; i < nums.length; i++) {
852 var branch = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + nums[i] + ".");
853 this._registerContentHandlerWithBranch(branch);
856 // We need to do this _after_ registering all of the available handlers,
857 // so that getWebContentHandlerByURI can return successfully.
858 try {
859 var autoBranch = ps.getBranch(PREF_CONTENTHANDLERS_AUTO);
860 var childPrefs = autoBranch.getChildList("", { });
861 for (var i = 0; i < childPrefs.length; ++i) {
862 var type = childPrefs[i];
863 var uri = autoBranch.getCharPref(type);
864 if (uri) {
865 var handler = this.getWebContentHandlerByURI(type, uri);
866 this._setAutoHandler(type, handler);
870 catch (e) {
871 // No auto branch yet, that's fine
872 //LOG("WCCR.init: There is no auto branch, benign");
877 * See nsIObserver
879 observe: function WCCR_observe(subject, topic, data) {
880 var os =
881 Cc["@mozilla.org/observer-service;1"].
882 getService(Ci.nsIObserverService);
883 switch (topic) {
884 case "app-startup":
885 os.addObserver(this, "browser-ui-startup-complete", false);
886 break;
887 case "browser-ui-startup-complete":
888 os.removeObserver(this, "browser-ui-startup-complete");
889 this._init();
890 break;
895 * See nsIFactory
897 createInstance: function WCCR_createInstance(outer, iid) {
898 if (outer != null)
899 throw Cr.NS_ERROR_NO_AGGREGATION;
900 return this.QueryInterface(iid);
904 * See nsIClassInfo
906 getInterfaces: function WCCR_getInterfaces(countRef) {
907 var interfaces =
908 [Ci.nsIWebContentConverterService, Ci.nsIWebContentHandlerRegistrar,
909 Ci.nsIObserver, Ci.nsIClassInfo, Ci.nsIFactory, Ci.nsISupports];
910 countRef.value = interfaces.length;
911 return interfaces;
913 getHelperForLanguage: function WCCR_getHelperForLanguage(language) {
914 return null;
916 contractID: WCCR_CONTRACTID,
917 classDescription: WCCR_CLASSNAME,
918 classID: WCCR_CLASSID,
919 implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
920 flags: Ci.nsIClassInfo.DOM_OBJECT,
923 * See nsISupports
925 QueryInterface: XPCOMUtils.generateQI(
926 [Ci.nsIWebContentConverterService,
927 Ci.nsIWebContentHandlerRegistrar,
928 Ci.nsIObserver,
929 Ci.nsIClassInfo,
930 Ci.nsIFactory,
931 Ci.nsISupports]),
933 _xpcom_categories: [{
934 category: "app-startup",
935 service: true
939 function NSGetModule(cm, file) {
940 return XPCOMUtils.generateModule([WebContentConverterRegistrar]);
943 #include ../../../../toolkit/content/debug.js