browser/components/feeds/src/FeedConverter.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/components/feeds/src/FeedConverter.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,583 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
    1.10 +Components.utils.import("resource://gre/modules/debug.js");
    1.11 +Components.utils.import("resource://gre/modules/Services.jsm");
    1.12 +
    1.13 +const Cc = Components.classes;
    1.14 +const Ci = Components.interfaces;
    1.15 +const Cr = Components.results;
    1.16 +
    1.17 +function LOG(str) {
    1.18 +  dump("*** " + str + "\n");
    1.19 +}
    1.20 +
    1.21 +const FS_CONTRACTID = "@mozilla.org/browser/feeds/result-service;1";
    1.22 +const FPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=feed";
    1.23 +const PCPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=pcast";
    1.24 +
    1.25 +const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
    1.26 +const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
    1.27 +const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
    1.28 +const TYPE_ANY = "*/*";
    1.29 +
    1.30 +const PREF_SELECTED_APP = "browser.feeds.handlers.application";
    1.31 +const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
    1.32 +const PREF_SELECTED_ACTION = "browser.feeds.handler";
    1.33 +const PREF_SELECTED_READER = "browser.feeds.handler.default";
    1.34 +
    1.35 +const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
    1.36 +const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
    1.37 +const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
    1.38 +const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
    1.39 +
    1.40 +const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
    1.41 +const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
    1.42 +const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
    1.43 +const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
    1.44 +
    1.45 +function getPrefAppForType(t) {
    1.46 +  switch (t) {
    1.47 +    case Ci.nsIFeed.TYPE_VIDEO:
    1.48 +      return PREF_VIDEO_SELECTED_APP;
    1.49 +
    1.50 +    case Ci.nsIFeed.TYPE_AUDIO:
    1.51 +      return PREF_AUDIO_SELECTED_APP;
    1.52 +
    1.53 +    default:
    1.54 +      return PREF_SELECTED_APP;
    1.55 +  }
    1.56 +}
    1.57 +
    1.58 +function getPrefWebForType(t) {
    1.59 +  switch (t) {
    1.60 +    case Ci.nsIFeed.TYPE_VIDEO:
    1.61 +      return PREF_VIDEO_SELECTED_WEB;
    1.62 +
    1.63 +    case Ci.nsIFeed.TYPE_AUDIO:
    1.64 +      return PREF_AUDIO_SELECTED_WEB;
    1.65 +
    1.66 +    default:
    1.67 +      return PREF_SELECTED_WEB;
    1.68 +  }
    1.69 +}
    1.70 +
    1.71 +function getPrefActionForType(t) {
    1.72 +  switch (t) {
    1.73 +    case Ci.nsIFeed.TYPE_VIDEO:
    1.74 +      return PREF_VIDEO_SELECTED_ACTION;
    1.75 +
    1.76 +    case Ci.nsIFeed.TYPE_AUDIO:
    1.77 +      return PREF_AUDIO_SELECTED_ACTION;
    1.78 +
    1.79 +    default:
    1.80 +      return PREF_SELECTED_ACTION;
    1.81 +  }
    1.82 +}
    1.83 +
    1.84 +function getPrefReaderForType(t) {
    1.85 +  switch (t) {
    1.86 +    case Ci.nsIFeed.TYPE_VIDEO:
    1.87 +      return PREF_VIDEO_SELECTED_READER;
    1.88 +
    1.89 +    case Ci.nsIFeed.TYPE_AUDIO:
    1.90 +      return PREF_AUDIO_SELECTED_READER;
    1.91 +
    1.92 +    default:
    1.93 +      return PREF_SELECTED_READER;
    1.94 +  }
    1.95 +}
    1.96 +
    1.97 +function safeGetCharPref(pref, defaultValue) {
    1.98 +  var prefs =   
    1.99 +      Cc["@mozilla.org/preferences-service;1"].
   1.100 +      getService(Ci.nsIPrefBranch);
   1.101 +  try {
   1.102 +    return prefs.getCharPref(pref);
   1.103 +  }
   1.104 +  catch (e) {
   1.105 +  }
   1.106 +  return defaultValue;
   1.107 +}
   1.108 +
   1.109 +function FeedConverter() {
   1.110 +}
   1.111 +FeedConverter.prototype = {
   1.112 +  classID: Components.ID("{229fa115-9412-4d32-baf3-2fc407f76fb1}"),
   1.113 +
   1.114 +  /**
   1.115 +   * This is the downloaded text data for the feed.
   1.116 +   */
   1.117 +  _data: null,
   1.118 +  
   1.119 +  /**
   1.120 +   * This is the object listening to the conversion, which is ultimately the
   1.121 +   * docshell for the load.
   1.122 +   */
   1.123 +  _listener: null,
   1.124 +
   1.125 +  /**
   1.126 +   * Records if the feed was sniffed
   1.127 +   */
   1.128 +  _sniffed: false,
   1.129 +
   1.130 +  /**
   1.131 +   * See nsIStreamConverter.idl
   1.132 +   */
   1.133 +  convert: function FC_convert(sourceStream, sourceType, destinationType, 
   1.134 +                               context) {
   1.135 +    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   1.136 +  },
   1.137 +  
   1.138 +  /**
   1.139 +   * See nsIStreamConverter.idl
   1.140 +   */
   1.141 +  asyncConvertData: function FC_asyncConvertData(sourceType, destinationType,
   1.142 +                                                 listener, context) {
   1.143 +    this._listener = listener;
   1.144 +  },
   1.145 +  
   1.146 +  /**
   1.147 +   * Whether or not the preview page is being forced.
   1.148 +   */
   1.149 +  _forcePreviewPage: false,
   1.150 +  
   1.151 +  /** 
   1.152 +   * Release our references to various things once we're done using them.
   1.153 +   */
   1.154 +  _releaseHandles: function FC__releaseHandles() {
   1.155 +    this._listener = null;
   1.156 +    this._request = null;
   1.157 +    this._processor = null;
   1.158 +  },
   1.159 +  
   1.160 +  /**
   1.161 +   * See nsIFeedResultListener.idl
   1.162 +   */
   1.163 +  handleResult: function FC_handleResult(result) {
   1.164 +    // Feeds come in various content types, which our feed sniffer coerces to
   1.165 +    // the maybe.feed type. However, feeds are used as a transport for 
   1.166 +    // different data types, e.g. news/blogs (traditional feed), video/audio
   1.167 +    // (podcasts) and photos (photocasts, photostreams). Each of these is 
   1.168 +    // different in that there's a different class of application suitable for
   1.169 +    // handling feeds of that type, but without a content-type differentiation
   1.170 +    // it is difficult for us to disambiguate.
   1.171 +    // 
   1.172 +    // The other problem is that if the user specifies an auto-action handler
   1.173 +    // for one feed application, the fact that the content type is shared means 
   1.174 +    // that all other applications will auto-load with that handler too, 
   1.175 +    // regardless of the content-type. 
   1.176 +    //
   1.177 +    // This means that content-type alone is not enough to determine whether
   1.178 +    // or not a feed should be auto-handled. This means that for feeds we need
   1.179 +    // to always use this stream converter, even when an auto-action is 
   1.180 +    // specified, not the basic one provided by WebContentConverter. This 
   1.181 +    // converter needs to consume all of the data and parse it, and based on
   1.182 +    // that determination make a judgment about type. 
   1.183 +    //
   1.184 +    // Since there are no content types for this content, and I'm not going to
   1.185 +    // invent any, the upshot is that while a user can set an auto-handler for
   1.186 +    // generic feed content, the system will prevent them from setting an auto-
   1.187 +    // handler for other stream types. In those cases, the user will always see
   1.188 +    // the preview page and have to select a handler. We can guess and show 
   1.189 +    // a client handler, but will not be able to show web handlers for those
   1.190 +    // types.
   1.191 +    //
   1.192 +    // If this is just a feed, not some kind of specialized application, then
   1.193 +    // auto-handlers can be set and we should obey them. 
   1.194 +    try {
   1.195 +      var feedService = 
   1.196 +          Cc["@mozilla.org/browser/feeds/result-service;1"].
   1.197 +          getService(Ci.nsIFeedResultService);
   1.198 +      if (!this._forcePreviewPage && result.doc) {
   1.199 +        var feed = result.doc.QueryInterface(Ci.nsIFeed);
   1.200 +        var handler = safeGetCharPref(getPrefActionForType(feed.type), "ask");
   1.201 +
   1.202 +        if (handler != "ask") {
   1.203 +          if (handler == "reader")
   1.204 +            handler = safeGetCharPref(getPrefReaderForType(feed.type), "bookmarks");
   1.205 +          switch (handler) {
   1.206 +            case "web":
   1.207 +              var wccr = 
   1.208 +                  Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
   1.209 +                  getService(Ci.nsIWebContentConverterService);
   1.210 +              if ((feed.type == Ci.nsIFeed.TYPE_FEED &&
   1.211 +                   wccr.getAutoHandler(TYPE_MAYBE_FEED)) ||
   1.212 +                  (feed.type == Ci.nsIFeed.TYPE_VIDEO &&
   1.213 +                   wccr.getAutoHandler(TYPE_MAYBE_VIDEO_FEED)) ||
   1.214 +                  (feed.type == Ci.nsIFeed.TYPE_AUDIO &&
   1.215 +                   wccr.getAutoHandler(TYPE_MAYBE_AUDIO_FEED))) {
   1.216 +                wccr.loadPreferredHandler(this._request);
   1.217 +                return;
   1.218 +              }
   1.219 +              break;
   1.220 +
   1.221 +            default:
   1.222 +              LOG("unexpected handler: " + handler);
   1.223 +              // fall through -- let feed service handle error
   1.224 +            case "bookmarks":
   1.225 +            case "client":
   1.226 +              try {
   1.227 +                var title = feed.title ? feed.title.plainText() : "";
   1.228 +                var desc = feed.subtitle ? feed.subtitle.plainText() : "";
   1.229 +                feedService.addToClientReader(result.uri.spec, title, desc, feed.type);
   1.230 +                return;
   1.231 +              } catch(ex) { /* fallback to preview mode */ }
   1.232 +          }
   1.233 +        }
   1.234 +      }
   1.235 +          
   1.236 +      var ios = 
   1.237 +          Cc["@mozilla.org/network/io-service;1"].
   1.238 +          getService(Ci.nsIIOService);
   1.239 +      var chromeChannel;
   1.240 +
   1.241 +      // If there was no automatic handler, or this was a podcast,
   1.242 +      // photostream or some other kind of application, show the preview page
   1.243 +      // if the parser returned a document.
   1.244 +      if (result.doc) {
   1.245 +
   1.246 +        // Store the result in the result service so that the display
   1.247 +        // page can access it.
   1.248 +        feedService.addFeedResult(result);
   1.249 +
   1.250 +        // Now load the actual XUL document.
   1.251 +        var aboutFeedsURI = ios.newURI("about:feeds", null, null);
   1.252 +        chromeChannel = ios.newChannelFromURI(aboutFeedsURI, null);
   1.253 +        chromeChannel.originalURI = result.uri;
   1.254 +        chromeChannel.owner =
   1.255 +          Services.scriptSecurityManager.getNoAppCodebasePrincipal(aboutFeedsURI);
   1.256 +      } else {
   1.257 +        chromeChannel = ios.newChannelFromURI(result.uri, null);
   1.258 +      }
   1.259 +
   1.260 +      chromeChannel.loadGroup = this._request.loadGroup;
   1.261 +      chromeChannel.asyncOpen(this._listener, null);
   1.262 +    }
   1.263 +    finally {
   1.264 +      this._releaseHandles();
   1.265 +    }
   1.266 +  },
   1.267 +  
   1.268 +  /**
   1.269 +   * See nsIStreamListener.idl
   1.270 +   */
   1.271 +  onDataAvailable: function FC_onDataAvailable(request, context, inputStream, 
   1.272 +                                               sourceOffset, count) {
   1.273 +    if (this._processor)
   1.274 +      this._processor.onDataAvailable(request, context, inputStream,
   1.275 +                                      sourceOffset, count);
   1.276 +  },
   1.277 +  
   1.278 +  /**
   1.279 +   * See nsIRequestObserver.idl
   1.280 +   */
   1.281 +  onStartRequest: function FC_onStartRequest(request, context) {
   1.282 +    var channel = request.QueryInterface(Ci.nsIChannel);
   1.283 +
   1.284 +    // Check for a header that tells us there was no sniffing
   1.285 +    // The value doesn't matter.
   1.286 +    try {
   1.287 +      var httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
   1.288 +      // Make sure to check requestSucceeded before the potentially-throwing
   1.289 +      // getResponseHeader.
   1.290 +      if (!httpChannel.requestSucceeded) {
   1.291 +        // Just give up, but don't forget to cancel the channel first!
   1.292 +        request.cancel(Cr.NS_BINDING_ABORTED);
   1.293 +        return;
   1.294 +      }
   1.295 +      var noSniff = httpChannel.getResponseHeader("X-Moz-Is-Feed");
   1.296 +    }
   1.297 +    catch (ex) {
   1.298 +      this._sniffed = true;
   1.299 +    }
   1.300 +
   1.301 +    this._request = request;
   1.302 +    
   1.303 +    // Save and reset the forced state bit early, in case there's some kind of
   1.304 +    // error.
   1.305 +    var feedService = 
   1.306 +        Cc["@mozilla.org/browser/feeds/result-service;1"].
   1.307 +        getService(Ci.nsIFeedResultService);
   1.308 +    this._forcePreviewPage = feedService.forcePreviewPage;
   1.309 +    feedService.forcePreviewPage = false;
   1.310 +
   1.311 +    // Parse feed data as it comes in
   1.312 +    this._processor =
   1.313 +        Cc["@mozilla.org/feed-processor;1"].
   1.314 +        createInstance(Ci.nsIFeedProcessor);
   1.315 +    this._processor.listener = this;
   1.316 +    this._processor.parseAsync(null, channel.URI);
   1.317 +    
   1.318 +    this._processor.onStartRequest(request, context);
   1.319 +  },
   1.320 +  
   1.321 +  /**
   1.322 +   * See nsIRequestObserver.idl
   1.323 +   */
   1.324 +  onStopRequest: function FC_onStopRequest(request, context, status) {
   1.325 +    if (this._processor)
   1.326 +      this._processor.onStopRequest(request, context, status);
   1.327 +  },
   1.328 +  
   1.329 +  /**
   1.330 +   * See nsISupports.idl
   1.331 +   */
   1.332 +  QueryInterface: function FC_QueryInterface(iid) {
   1.333 +    if (iid.equals(Ci.nsIFeedResultListener) ||
   1.334 +        iid.equals(Ci.nsIStreamConverter) ||
   1.335 +        iid.equals(Ci.nsIStreamListener) ||
   1.336 +        iid.equals(Ci.nsIRequestObserver)||
   1.337 +        iid.equals(Ci.nsISupports))
   1.338 +      return this;
   1.339 +    throw Cr.NS_ERROR_NO_INTERFACE;
   1.340 +  },
   1.341 +};
   1.342 +
   1.343 +/**
   1.344 + * Keeps parsed FeedResults around for use elsewhere in the UI after the stream
   1.345 + * converter completes. 
   1.346 + */
   1.347 +function FeedResultService() {
   1.348 +}
   1.349 +
   1.350 +FeedResultService.prototype = {
   1.351 +  classID: Components.ID("{2376201c-bbc6-472f-9b62-7548040a61c6}"),
   1.352 +  
   1.353 +  /**
   1.354 +   * A URI spec -> [nsIFeedResult] hash. We have to keep a list as the
   1.355 +   * value in case the same URI is requested concurrently.
   1.356 +   */
   1.357 +  _results: { },
   1.358 +  
   1.359 +  /**
   1.360 +   * See nsIFeedResultService.idl
   1.361 +   */
   1.362 +  forcePreviewPage: false,
   1.363 +  
   1.364 +  /**
   1.365 +   * See nsIFeedResultService.idl
   1.366 +   */
   1.367 +  addToClientReader: function FRS_addToClientReader(spec, title, subtitle, feedType) {
   1.368 +    var prefs =   
   1.369 +        Cc["@mozilla.org/preferences-service;1"].
   1.370 +        getService(Ci.nsIPrefBranch);
   1.371 +
   1.372 +    var handler = safeGetCharPref(getPrefActionForType(feedType), "bookmarks");
   1.373 +    if (handler == "ask" || handler == "reader")
   1.374 +      handler = safeGetCharPref(getPrefReaderForType(feedType), "bookmarks");
   1.375 +
   1.376 +    switch (handler) {
   1.377 +    case "client":
   1.378 +      var clientApp = prefs.getComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile);
   1.379 +
   1.380 +      // For the benefit of applications that might know how to deal with more
   1.381 +      // URLs than just feeds, send feed: URLs in the following format:
   1.382 +      //
   1.383 +      // http urls: replace scheme with feed, e.g.
   1.384 +      // http://foo.com/index.rdf -> feed://foo.com/index.rdf
   1.385 +      // other urls: prepend feed: scheme, e.g.
   1.386 +      // https://foo.com/index.rdf -> feed:https://foo.com/index.rdf
   1.387 +      var ios = 
   1.388 +          Cc["@mozilla.org/network/io-service;1"].
   1.389 +          getService(Ci.nsIIOService);
   1.390 +      var feedURI = ios.newURI(spec, null, null);
   1.391 +      if (feedURI.schemeIs("http")) {
   1.392 +        feedURI.scheme = "feed";
   1.393 +        spec = feedURI.spec;
   1.394 +      }
   1.395 +      else
   1.396 +        spec = "feed:" + spec;
   1.397 +
   1.398 +      // Retrieving the shell service might fail on some systems, most
   1.399 +      // notably systems where GNOME is not installed.
   1.400 +      try {
   1.401 +        var ss =
   1.402 +            Cc["@mozilla.org/browser/shell-service;1"].
   1.403 +            getService(Ci.nsIShellService);
   1.404 +        ss.openApplicationWithURI(clientApp, spec);
   1.405 +      } catch(e) {
   1.406 +        // If we couldn't use the shell service, fallback to using a
   1.407 +        // nsIProcess instance
   1.408 +        var p =
   1.409 +            Cc["@mozilla.org/process/util;1"].
   1.410 +            createInstance(Ci.nsIProcess);
   1.411 +        p.init(clientApp);
   1.412 +        p.run(false, [spec], 1);
   1.413 +      }
   1.414 +      break;
   1.415 +
   1.416 +    default:
   1.417 +      // "web" should have been handled elsewhere
   1.418 +      LOG("unexpected handler: " + handler);
   1.419 +      // fall through
   1.420 +    case "bookmarks":
   1.421 +      var wm = 
   1.422 +          Cc["@mozilla.org/appshell/window-mediator;1"].
   1.423 +          getService(Ci.nsIWindowMediator);
   1.424 +      var topWindow = wm.getMostRecentWindow("navigator:browser");
   1.425 +      topWindow.PlacesCommandHook.addLiveBookmark(spec, title, subtitle);
   1.426 +      break;
   1.427 +    }
   1.428 +  },
   1.429 +  
   1.430 +  /**
   1.431 +   * See nsIFeedResultService.idl
   1.432 +   */
   1.433 +  addFeedResult: function FRS_addFeedResult(feedResult) {
   1.434 +    NS_ASSERT(feedResult.uri != null, "null URI!");
   1.435 +    NS_ASSERT(feedResult.uri != null, "null feedResult!");
   1.436 +    var spec = feedResult.uri.spec;
   1.437 +    if(!this._results[spec])  
   1.438 +      this._results[spec] = [];
   1.439 +    this._results[spec].push(feedResult);
   1.440 +  },
   1.441 +  
   1.442 +  /**
   1.443 +   * See nsIFeedResultService.idl
   1.444 +   */
   1.445 +  getFeedResult: function RFS_getFeedResult(uri) {
   1.446 +    NS_ASSERT(uri != null, "null URI!");
   1.447 +    var resultList = this._results[uri.spec];
   1.448 +    for (var i in resultList) {
   1.449 +      if (resultList[i].uri == uri)
   1.450 +        return resultList[i];
   1.451 +    }
   1.452 +    return null;
   1.453 +  },
   1.454 +  
   1.455 +  /**
   1.456 +   * See nsIFeedResultService.idl
   1.457 +   */
   1.458 +  removeFeedResult: function FRS_removeFeedResult(uri) {
   1.459 +    NS_ASSERT(uri != null, "null URI!");
   1.460 +    var resultList = this._results[uri.spec];
   1.461 +    if (!resultList)
   1.462 +      return;
   1.463 +    var deletions = 0;
   1.464 +    for (var i = 0; i < resultList.length; ++i) {
   1.465 +      if (resultList[i].uri == uri) {
   1.466 +        delete resultList[i];
   1.467 +        ++deletions;
   1.468 +      }
   1.469 +    }
   1.470 +    
   1.471 +    // send the holes to the end
   1.472 +    resultList.sort();
   1.473 +    // and trim the list
   1.474 +    resultList.splice(resultList.length - deletions, deletions);
   1.475 +    if (resultList.length == 0)
   1.476 +      delete this._results[uri.spec];
   1.477 +  },
   1.478 +
   1.479 +  createInstance: function FRS_createInstance(outer, iid) {
   1.480 +    if (outer != null)
   1.481 +      throw Cr.NS_ERROR_NO_AGGREGATION;
   1.482 +    return this.QueryInterface(iid);
   1.483 +  },
   1.484 +  
   1.485 +  QueryInterface: function FRS_QueryInterface(iid) {
   1.486 +    if (iid.equals(Ci.nsIFeedResultService) ||
   1.487 +        iid.equals(Ci.nsIFactory) ||
   1.488 +        iid.equals(Ci.nsISupports))
   1.489 +      return this;
   1.490 +    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   1.491 +  },
   1.492 +};
   1.493 +
   1.494 +/**
   1.495 + * A protocol handler that attempts to deal with the variant forms of feed:
   1.496 + * URIs that are actually either http or https.
   1.497 + */
   1.498 +function GenericProtocolHandler() {
   1.499 +}
   1.500 +GenericProtocolHandler.prototype = {
   1.501 +  _init: function GPH_init(scheme) {
   1.502 +    var ios = 
   1.503 +      Cc["@mozilla.org/network/io-service;1"].
   1.504 +      getService(Ci.nsIIOService);
   1.505 +    this._http = ios.getProtocolHandler("http");
   1.506 +    this._scheme = scheme;
   1.507 +  },
   1.508 +
   1.509 +  get scheme() {
   1.510 +    return this._scheme;
   1.511 +  },
   1.512 +  
   1.513 +  get protocolFlags() {
   1.514 +    return this._http.protocolFlags;
   1.515 +  },
   1.516 +  
   1.517 +  get defaultPort() {
   1.518 +    return this._http.defaultPort;
   1.519 +  },
   1.520 +  
   1.521 +  allowPort: function GPH_allowPort(port, scheme) {
   1.522 +    return this._http.allowPort(port, scheme);
   1.523 +  },
   1.524 +  
   1.525 +  newURI: function GPH_newURI(spec, originalCharset, baseURI) {
   1.526 +    // Feed URIs can be either nested URIs of the form feed:realURI (in which
   1.527 +    // case we create a nested URI for the realURI) or feed://example.com, in
   1.528 +    // which case we create a nested URI for the real protocol which is http.
   1.529 +
   1.530 +    var scheme = this._scheme + ":";
   1.531 +    if (spec.substr(0, scheme.length) != scheme)
   1.532 +      throw Cr.NS_ERROR_MALFORMED_URI;
   1.533 +
   1.534 +    var prefix = spec.substr(scheme.length, 2) == "//" ? "http:" : "";
   1.535 +    var inner = Cc["@mozilla.org/network/io-service;1"].
   1.536 +                getService(Ci.nsIIOService).newURI(spec.replace(scheme, prefix),
   1.537 +                                                   originalCharset, baseURI);
   1.538 +    var netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil);
   1.539 +    const URI_INHERITS_SECURITY_CONTEXT = Ci.nsIProtocolHandler
   1.540 +                                            .URI_INHERITS_SECURITY_CONTEXT;
   1.541 +    if (netutil.URIChainHasFlags(inner, URI_INHERITS_SECURITY_CONTEXT))
   1.542 +      throw Cr.NS_ERROR_MALFORMED_URI;
   1.543 +
   1.544 +    var uri = netutil.newSimpleNestedURI(inner);
   1.545 +    uri.spec = inner.spec.replace(prefix, scheme);
   1.546 +    return uri;
   1.547 +  },
   1.548 +  
   1.549 +  newChannel: function GPH_newChannel(aUri) {
   1.550 +    var inner = aUri.QueryInterface(Ci.nsINestedURI).innerURI;
   1.551 +    var channel = Cc["@mozilla.org/network/io-service;1"].
   1.552 +                  getService(Ci.nsIIOService).newChannelFromURI(inner, null);
   1.553 +    if (channel instanceof Components.interfaces.nsIHttpChannel)
   1.554 +      // Set this so we know this is supposed to be a feed
   1.555 +      channel.setRequestHeader("X-Moz-Is-Feed", "1", false);
   1.556 +    channel.originalURI = aUri;
   1.557 +    return channel;
   1.558 +  },
   1.559 +  
   1.560 +  QueryInterface: function GPH_QueryInterface(iid) {
   1.561 +    if (iid.equals(Ci.nsIProtocolHandler) ||
   1.562 +        iid.equals(Ci.nsISupports))
   1.563 +      return this;
   1.564 +    throw Cr.NS_ERROR_NO_INTERFACE;
   1.565 +  }  
   1.566 +};
   1.567 +
   1.568 +function FeedProtocolHandler() {
   1.569 +  this._init('feed');
   1.570 +}
   1.571 +FeedProtocolHandler.prototype = new GenericProtocolHandler();
   1.572 +FeedProtocolHandler.prototype.classID = Components.ID("{4f91ef2e-57ba-472e-ab7a-b4999e42d6c0}");
   1.573 +
   1.574 +function PodCastProtocolHandler() {
   1.575 +  this._init('pcast');
   1.576 +}
   1.577 +PodCastProtocolHandler.prototype = new GenericProtocolHandler();
   1.578 +PodCastProtocolHandler.prototype.classID = Components.ID("{1c31ed79-accd-4b94-b517-06e0c81999d5}");
   1.579 +
   1.580 +var components = [FeedConverter,
   1.581 +                  FeedResultService,
   1.582 +                  FeedProtocolHandler,
   1.583 +                  PodCastProtocolHandler];
   1.584 +
   1.585 +
   1.586 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

mercurial