browser/components/feeds/src/FeedConverter.js

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 7 Components.utils.import("resource://gre/modules/debug.js");
michael@0 8 Components.utils.import("resource://gre/modules/Services.jsm");
michael@0 9
michael@0 10 const Cc = Components.classes;
michael@0 11 const Ci = Components.interfaces;
michael@0 12 const Cr = Components.results;
michael@0 13
michael@0 14 function LOG(str) {
michael@0 15 dump("*** " + str + "\n");
michael@0 16 }
michael@0 17
michael@0 18 const FS_CONTRACTID = "@mozilla.org/browser/feeds/result-service;1";
michael@0 19 const FPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=feed";
michael@0 20 const PCPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=pcast";
michael@0 21
michael@0 22 const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
michael@0 23 const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
michael@0 24 const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
michael@0 25 const TYPE_ANY = "*/*";
michael@0 26
michael@0 27 const PREF_SELECTED_APP = "browser.feeds.handlers.application";
michael@0 28 const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
michael@0 29 const PREF_SELECTED_ACTION = "browser.feeds.handler";
michael@0 30 const PREF_SELECTED_READER = "browser.feeds.handler.default";
michael@0 31
michael@0 32 const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
michael@0 33 const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
michael@0 34 const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
michael@0 35 const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
michael@0 36
michael@0 37 const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
michael@0 38 const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
michael@0 39 const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
michael@0 40 const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
michael@0 41
michael@0 42 function getPrefAppForType(t) {
michael@0 43 switch (t) {
michael@0 44 case Ci.nsIFeed.TYPE_VIDEO:
michael@0 45 return PREF_VIDEO_SELECTED_APP;
michael@0 46
michael@0 47 case Ci.nsIFeed.TYPE_AUDIO:
michael@0 48 return PREF_AUDIO_SELECTED_APP;
michael@0 49
michael@0 50 default:
michael@0 51 return PREF_SELECTED_APP;
michael@0 52 }
michael@0 53 }
michael@0 54
michael@0 55 function getPrefWebForType(t) {
michael@0 56 switch (t) {
michael@0 57 case Ci.nsIFeed.TYPE_VIDEO:
michael@0 58 return PREF_VIDEO_SELECTED_WEB;
michael@0 59
michael@0 60 case Ci.nsIFeed.TYPE_AUDIO:
michael@0 61 return PREF_AUDIO_SELECTED_WEB;
michael@0 62
michael@0 63 default:
michael@0 64 return PREF_SELECTED_WEB;
michael@0 65 }
michael@0 66 }
michael@0 67
michael@0 68 function getPrefActionForType(t) {
michael@0 69 switch (t) {
michael@0 70 case Ci.nsIFeed.TYPE_VIDEO:
michael@0 71 return PREF_VIDEO_SELECTED_ACTION;
michael@0 72
michael@0 73 case Ci.nsIFeed.TYPE_AUDIO:
michael@0 74 return PREF_AUDIO_SELECTED_ACTION;
michael@0 75
michael@0 76 default:
michael@0 77 return PREF_SELECTED_ACTION;
michael@0 78 }
michael@0 79 }
michael@0 80
michael@0 81 function getPrefReaderForType(t) {
michael@0 82 switch (t) {
michael@0 83 case Ci.nsIFeed.TYPE_VIDEO:
michael@0 84 return PREF_VIDEO_SELECTED_READER;
michael@0 85
michael@0 86 case Ci.nsIFeed.TYPE_AUDIO:
michael@0 87 return PREF_AUDIO_SELECTED_READER;
michael@0 88
michael@0 89 default:
michael@0 90 return PREF_SELECTED_READER;
michael@0 91 }
michael@0 92 }
michael@0 93
michael@0 94 function safeGetCharPref(pref, defaultValue) {
michael@0 95 var prefs =
michael@0 96 Cc["@mozilla.org/preferences-service;1"].
michael@0 97 getService(Ci.nsIPrefBranch);
michael@0 98 try {
michael@0 99 return prefs.getCharPref(pref);
michael@0 100 }
michael@0 101 catch (e) {
michael@0 102 }
michael@0 103 return defaultValue;
michael@0 104 }
michael@0 105
michael@0 106 function FeedConverter() {
michael@0 107 }
michael@0 108 FeedConverter.prototype = {
michael@0 109 classID: Components.ID("{229fa115-9412-4d32-baf3-2fc407f76fb1}"),
michael@0 110
michael@0 111 /**
michael@0 112 * This is the downloaded text data for the feed.
michael@0 113 */
michael@0 114 _data: null,
michael@0 115
michael@0 116 /**
michael@0 117 * This is the object listening to the conversion, which is ultimately the
michael@0 118 * docshell for the load.
michael@0 119 */
michael@0 120 _listener: null,
michael@0 121
michael@0 122 /**
michael@0 123 * Records if the feed was sniffed
michael@0 124 */
michael@0 125 _sniffed: false,
michael@0 126
michael@0 127 /**
michael@0 128 * See nsIStreamConverter.idl
michael@0 129 */
michael@0 130 convert: function FC_convert(sourceStream, sourceType, destinationType,
michael@0 131 context) {
michael@0 132 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
michael@0 133 },
michael@0 134
michael@0 135 /**
michael@0 136 * See nsIStreamConverter.idl
michael@0 137 */
michael@0 138 asyncConvertData: function FC_asyncConvertData(sourceType, destinationType,
michael@0 139 listener, context) {
michael@0 140 this._listener = listener;
michael@0 141 },
michael@0 142
michael@0 143 /**
michael@0 144 * Whether or not the preview page is being forced.
michael@0 145 */
michael@0 146 _forcePreviewPage: false,
michael@0 147
michael@0 148 /**
michael@0 149 * Release our references to various things once we're done using them.
michael@0 150 */
michael@0 151 _releaseHandles: function FC__releaseHandles() {
michael@0 152 this._listener = null;
michael@0 153 this._request = null;
michael@0 154 this._processor = null;
michael@0 155 },
michael@0 156
michael@0 157 /**
michael@0 158 * See nsIFeedResultListener.idl
michael@0 159 */
michael@0 160 handleResult: function FC_handleResult(result) {
michael@0 161 // Feeds come in various content types, which our feed sniffer coerces to
michael@0 162 // the maybe.feed type. However, feeds are used as a transport for
michael@0 163 // different data types, e.g. news/blogs (traditional feed), video/audio
michael@0 164 // (podcasts) and photos (photocasts, photostreams). Each of these is
michael@0 165 // different in that there's a different class of application suitable for
michael@0 166 // handling feeds of that type, but without a content-type differentiation
michael@0 167 // it is difficult for us to disambiguate.
michael@0 168 //
michael@0 169 // The other problem is that if the user specifies an auto-action handler
michael@0 170 // for one feed application, the fact that the content type is shared means
michael@0 171 // that all other applications will auto-load with that handler too,
michael@0 172 // regardless of the content-type.
michael@0 173 //
michael@0 174 // This means that content-type alone is not enough to determine whether
michael@0 175 // or not a feed should be auto-handled. This means that for feeds we need
michael@0 176 // to always use this stream converter, even when an auto-action is
michael@0 177 // specified, not the basic one provided by WebContentConverter. This
michael@0 178 // converter needs to consume all of the data and parse it, and based on
michael@0 179 // that determination make a judgment about type.
michael@0 180 //
michael@0 181 // Since there are no content types for this content, and I'm not going to
michael@0 182 // invent any, the upshot is that while a user can set an auto-handler for
michael@0 183 // generic feed content, the system will prevent them from setting an auto-
michael@0 184 // handler for other stream types. In those cases, the user will always see
michael@0 185 // the preview page and have to select a handler. We can guess and show
michael@0 186 // a client handler, but will not be able to show web handlers for those
michael@0 187 // types.
michael@0 188 //
michael@0 189 // If this is just a feed, not some kind of specialized application, then
michael@0 190 // auto-handlers can be set and we should obey them.
michael@0 191 try {
michael@0 192 var feedService =
michael@0 193 Cc["@mozilla.org/browser/feeds/result-service;1"].
michael@0 194 getService(Ci.nsIFeedResultService);
michael@0 195 if (!this._forcePreviewPage && result.doc) {
michael@0 196 var feed = result.doc.QueryInterface(Ci.nsIFeed);
michael@0 197 var handler = safeGetCharPref(getPrefActionForType(feed.type), "ask");
michael@0 198
michael@0 199 if (handler != "ask") {
michael@0 200 if (handler == "reader")
michael@0 201 handler = safeGetCharPref(getPrefReaderForType(feed.type), "bookmarks");
michael@0 202 switch (handler) {
michael@0 203 case "web":
michael@0 204 var wccr =
michael@0 205 Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
michael@0 206 getService(Ci.nsIWebContentConverterService);
michael@0 207 if ((feed.type == Ci.nsIFeed.TYPE_FEED &&
michael@0 208 wccr.getAutoHandler(TYPE_MAYBE_FEED)) ||
michael@0 209 (feed.type == Ci.nsIFeed.TYPE_VIDEO &&
michael@0 210 wccr.getAutoHandler(TYPE_MAYBE_VIDEO_FEED)) ||
michael@0 211 (feed.type == Ci.nsIFeed.TYPE_AUDIO &&
michael@0 212 wccr.getAutoHandler(TYPE_MAYBE_AUDIO_FEED))) {
michael@0 213 wccr.loadPreferredHandler(this._request);
michael@0 214 return;
michael@0 215 }
michael@0 216 break;
michael@0 217
michael@0 218 default:
michael@0 219 LOG("unexpected handler: " + handler);
michael@0 220 // fall through -- let feed service handle error
michael@0 221 case "bookmarks":
michael@0 222 case "client":
michael@0 223 try {
michael@0 224 var title = feed.title ? feed.title.plainText() : "";
michael@0 225 var desc = feed.subtitle ? feed.subtitle.plainText() : "";
michael@0 226 feedService.addToClientReader(result.uri.spec, title, desc, feed.type);
michael@0 227 return;
michael@0 228 } catch(ex) { /* fallback to preview mode */ }
michael@0 229 }
michael@0 230 }
michael@0 231 }
michael@0 232
michael@0 233 var ios =
michael@0 234 Cc["@mozilla.org/network/io-service;1"].
michael@0 235 getService(Ci.nsIIOService);
michael@0 236 var chromeChannel;
michael@0 237
michael@0 238 // If there was no automatic handler, or this was a podcast,
michael@0 239 // photostream or some other kind of application, show the preview page
michael@0 240 // if the parser returned a document.
michael@0 241 if (result.doc) {
michael@0 242
michael@0 243 // Store the result in the result service so that the display
michael@0 244 // page can access it.
michael@0 245 feedService.addFeedResult(result);
michael@0 246
michael@0 247 // Now load the actual XUL document.
michael@0 248 var aboutFeedsURI = ios.newURI("about:feeds", null, null);
michael@0 249 chromeChannel = ios.newChannelFromURI(aboutFeedsURI, null);
michael@0 250 chromeChannel.originalURI = result.uri;
michael@0 251 chromeChannel.owner =
michael@0 252 Services.scriptSecurityManager.getNoAppCodebasePrincipal(aboutFeedsURI);
michael@0 253 } else {
michael@0 254 chromeChannel = ios.newChannelFromURI(result.uri, null);
michael@0 255 }
michael@0 256
michael@0 257 chromeChannel.loadGroup = this._request.loadGroup;
michael@0 258 chromeChannel.asyncOpen(this._listener, null);
michael@0 259 }
michael@0 260 finally {
michael@0 261 this._releaseHandles();
michael@0 262 }
michael@0 263 },
michael@0 264
michael@0 265 /**
michael@0 266 * See nsIStreamListener.idl
michael@0 267 */
michael@0 268 onDataAvailable: function FC_onDataAvailable(request, context, inputStream,
michael@0 269 sourceOffset, count) {
michael@0 270 if (this._processor)
michael@0 271 this._processor.onDataAvailable(request, context, inputStream,
michael@0 272 sourceOffset, count);
michael@0 273 },
michael@0 274
michael@0 275 /**
michael@0 276 * See nsIRequestObserver.idl
michael@0 277 */
michael@0 278 onStartRequest: function FC_onStartRequest(request, context) {
michael@0 279 var channel = request.QueryInterface(Ci.nsIChannel);
michael@0 280
michael@0 281 // Check for a header that tells us there was no sniffing
michael@0 282 // The value doesn't matter.
michael@0 283 try {
michael@0 284 var httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
michael@0 285 // Make sure to check requestSucceeded before the potentially-throwing
michael@0 286 // getResponseHeader.
michael@0 287 if (!httpChannel.requestSucceeded) {
michael@0 288 // Just give up, but don't forget to cancel the channel first!
michael@0 289 request.cancel(Cr.NS_BINDING_ABORTED);
michael@0 290 return;
michael@0 291 }
michael@0 292 var noSniff = httpChannel.getResponseHeader("X-Moz-Is-Feed");
michael@0 293 }
michael@0 294 catch (ex) {
michael@0 295 this._sniffed = true;
michael@0 296 }
michael@0 297
michael@0 298 this._request = request;
michael@0 299
michael@0 300 // Save and reset the forced state bit early, in case there's some kind of
michael@0 301 // error.
michael@0 302 var feedService =
michael@0 303 Cc["@mozilla.org/browser/feeds/result-service;1"].
michael@0 304 getService(Ci.nsIFeedResultService);
michael@0 305 this._forcePreviewPage = feedService.forcePreviewPage;
michael@0 306 feedService.forcePreviewPage = false;
michael@0 307
michael@0 308 // Parse feed data as it comes in
michael@0 309 this._processor =
michael@0 310 Cc["@mozilla.org/feed-processor;1"].
michael@0 311 createInstance(Ci.nsIFeedProcessor);
michael@0 312 this._processor.listener = this;
michael@0 313 this._processor.parseAsync(null, channel.URI);
michael@0 314
michael@0 315 this._processor.onStartRequest(request, context);
michael@0 316 },
michael@0 317
michael@0 318 /**
michael@0 319 * See nsIRequestObserver.idl
michael@0 320 */
michael@0 321 onStopRequest: function FC_onStopRequest(request, context, status) {
michael@0 322 if (this._processor)
michael@0 323 this._processor.onStopRequest(request, context, status);
michael@0 324 },
michael@0 325
michael@0 326 /**
michael@0 327 * See nsISupports.idl
michael@0 328 */
michael@0 329 QueryInterface: function FC_QueryInterface(iid) {
michael@0 330 if (iid.equals(Ci.nsIFeedResultListener) ||
michael@0 331 iid.equals(Ci.nsIStreamConverter) ||
michael@0 332 iid.equals(Ci.nsIStreamListener) ||
michael@0 333 iid.equals(Ci.nsIRequestObserver)||
michael@0 334 iid.equals(Ci.nsISupports))
michael@0 335 return this;
michael@0 336 throw Cr.NS_ERROR_NO_INTERFACE;
michael@0 337 },
michael@0 338 };
michael@0 339
michael@0 340 /**
michael@0 341 * Keeps parsed FeedResults around for use elsewhere in the UI after the stream
michael@0 342 * converter completes.
michael@0 343 */
michael@0 344 function FeedResultService() {
michael@0 345 }
michael@0 346
michael@0 347 FeedResultService.prototype = {
michael@0 348 classID: Components.ID("{2376201c-bbc6-472f-9b62-7548040a61c6}"),
michael@0 349
michael@0 350 /**
michael@0 351 * A URI spec -> [nsIFeedResult] hash. We have to keep a list as the
michael@0 352 * value in case the same URI is requested concurrently.
michael@0 353 */
michael@0 354 _results: { },
michael@0 355
michael@0 356 /**
michael@0 357 * See nsIFeedResultService.idl
michael@0 358 */
michael@0 359 forcePreviewPage: false,
michael@0 360
michael@0 361 /**
michael@0 362 * See nsIFeedResultService.idl
michael@0 363 */
michael@0 364 addToClientReader: function FRS_addToClientReader(spec, title, subtitle, feedType) {
michael@0 365 var prefs =
michael@0 366 Cc["@mozilla.org/preferences-service;1"].
michael@0 367 getService(Ci.nsIPrefBranch);
michael@0 368
michael@0 369 var handler = safeGetCharPref(getPrefActionForType(feedType), "bookmarks");
michael@0 370 if (handler == "ask" || handler == "reader")
michael@0 371 handler = safeGetCharPref(getPrefReaderForType(feedType), "bookmarks");
michael@0 372
michael@0 373 switch (handler) {
michael@0 374 case "client":
michael@0 375 var clientApp = prefs.getComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile);
michael@0 376
michael@0 377 // For the benefit of applications that might know how to deal with more
michael@0 378 // URLs than just feeds, send feed: URLs in the following format:
michael@0 379 //
michael@0 380 // http urls: replace scheme with feed, e.g.
michael@0 381 // http://foo.com/index.rdf -> feed://foo.com/index.rdf
michael@0 382 // other urls: prepend feed: scheme, e.g.
michael@0 383 // https://foo.com/index.rdf -> feed:https://foo.com/index.rdf
michael@0 384 var ios =
michael@0 385 Cc["@mozilla.org/network/io-service;1"].
michael@0 386 getService(Ci.nsIIOService);
michael@0 387 var feedURI = ios.newURI(spec, null, null);
michael@0 388 if (feedURI.schemeIs("http")) {
michael@0 389 feedURI.scheme = "feed";
michael@0 390 spec = feedURI.spec;
michael@0 391 }
michael@0 392 else
michael@0 393 spec = "feed:" + spec;
michael@0 394
michael@0 395 // Retrieving the shell service might fail on some systems, most
michael@0 396 // notably systems where GNOME is not installed.
michael@0 397 try {
michael@0 398 var ss =
michael@0 399 Cc["@mozilla.org/browser/shell-service;1"].
michael@0 400 getService(Ci.nsIShellService);
michael@0 401 ss.openApplicationWithURI(clientApp, spec);
michael@0 402 } catch(e) {
michael@0 403 // If we couldn't use the shell service, fallback to using a
michael@0 404 // nsIProcess instance
michael@0 405 var p =
michael@0 406 Cc["@mozilla.org/process/util;1"].
michael@0 407 createInstance(Ci.nsIProcess);
michael@0 408 p.init(clientApp);
michael@0 409 p.run(false, [spec], 1);
michael@0 410 }
michael@0 411 break;
michael@0 412
michael@0 413 default:
michael@0 414 // "web" should have been handled elsewhere
michael@0 415 LOG("unexpected handler: " + handler);
michael@0 416 // fall through
michael@0 417 case "bookmarks":
michael@0 418 var wm =
michael@0 419 Cc["@mozilla.org/appshell/window-mediator;1"].
michael@0 420 getService(Ci.nsIWindowMediator);
michael@0 421 var topWindow = wm.getMostRecentWindow("navigator:browser");
michael@0 422 topWindow.PlacesCommandHook.addLiveBookmark(spec, title, subtitle);
michael@0 423 break;
michael@0 424 }
michael@0 425 },
michael@0 426
michael@0 427 /**
michael@0 428 * See nsIFeedResultService.idl
michael@0 429 */
michael@0 430 addFeedResult: function FRS_addFeedResult(feedResult) {
michael@0 431 NS_ASSERT(feedResult.uri != null, "null URI!");
michael@0 432 NS_ASSERT(feedResult.uri != null, "null feedResult!");
michael@0 433 var spec = feedResult.uri.spec;
michael@0 434 if(!this._results[spec])
michael@0 435 this._results[spec] = [];
michael@0 436 this._results[spec].push(feedResult);
michael@0 437 },
michael@0 438
michael@0 439 /**
michael@0 440 * See nsIFeedResultService.idl
michael@0 441 */
michael@0 442 getFeedResult: function RFS_getFeedResult(uri) {
michael@0 443 NS_ASSERT(uri != null, "null URI!");
michael@0 444 var resultList = this._results[uri.spec];
michael@0 445 for (var i in resultList) {
michael@0 446 if (resultList[i].uri == uri)
michael@0 447 return resultList[i];
michael@0 448 }
michael@0 449 return null;
michael@0 450 },
michael@0 451
michael@0 452 /**
michael@0 453 * See nsIFeedResultService.idl
michael@0 454 */
michael@0 455 removeFeedResult: function FRS_removeFeedResult(uri) {
michael@0 456 NS_ASSERT(uri != null, "null URI!");
michael@0 457 var resultList = this._results[uri.spec];
michael@0 458 if (!resultList)
michael@0 459 return;
michael@0 460 var deletions = 0;
michael@0 461 for (var i = 0; i < resultList.length; ++i) {
michael@0 462 if (resultList[i].uri == uri) {
michael@0 463 delete resultList[i];
michael@0 464 ++deletions;
michael@0 465 }
michael@0 466 }
michael@0 467
michael@0 468 // send the holes to the end
michael@0 469 resultList.sort();
michael@0 470 // and trim the list
michael@0 471 resultList.splice(resultList.length - deletions, deletions);
michael@0 472 if (resultList.length == 0)
michael@0 473 delete this._results[uri.spec];
michael@0 474 },
michael@0 475
michael@0 476 createInstance: function FRS_createInstance(outer, iid) {
michael@0 477 if (outer != null)
michael@0 478 throw Cr.NS_ERROR_NO_AGGREGATION;
michael@0 479 return this.QueryInterface(iid);
michael@0 480 },
michael@0 481
michael@0 482 QueryInterface: function FRS_QueryInterface(iid) {
michael@0 483 if (iid.equals(Ci.nsIFeedResultService) ||
michael@0 484 iid.equals(Ci.nsIFactory) ||
michael@0 485 iid.equals(Ci.nsISupports))
michael@0 486 return this;
michael@0 487 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
michael@0 488 },
michael@0 489 };
michael@0 490
michael@0 491 /**
michael@0 492 * A protocol handler that attempts to deal with the variant forms of feed:
michael@0 493 * URIs that are actually either http or https.
michael@0 494 */
michael@0 495 function GenericProtocolHandler() {
michael@0 496 }
michael@0 497 GenericProtocolHandler.prototype = {
michael@0 498 _init: function GPH_init(scheme) {
michael@0 499 var ios =
michael@0 500 Cc["@mozilla.org/network/io-service;1"].
michael@0 501 getService(Ci.nsIIOService);
michael@0 502 this._http = ios.getProtocolHandler("http");
michael@0 503 this._scheme = scheme;
michael@0 504 },
michael@0 505
michael@0 506 get scheme() {
michael@0 507 return this._scheme;
michael@0 508 },
michael@0 509
michael@0 510 get protocolFlags() {
michael@0 511 return this._http.protocolFlags;
michael@0 512 },
michael@0 513
michael@0 514 get defaultPort() {
michael@0 515 return this._http.defaultPort;
michael@0 516 },
michael@0 517
michael@0 518 allowPort: function GPH_allowPort(port, scheme) {
michael@0 519 return this._http.allowPort(port, scheme);
michael@0 520 },
michael@0 521
michael@0 522 newURI: function GPH_newURI(spec, originalCharset, baseURI) {
michael@0 523 // Feed URIs can be either nested URIs of the form feed:realURI (in which
michael@0 524 // case we create a nested URI for the realURI) or feed://example.com, in
michael@0 525 // which case we create a nested URI for the real protocol which is http.
michael@0 526
michael@0 527 var scheme = this._scheme + ":";
michael@0 528 if (spec.substr(0, scheme.length) != scheme)
michael@0 529 throw Cr.NS_ERROR_MALFORMED_URI;
michael@0 530
michael@0 531 var prefix = spec.substr(scheme.length, 2) == "//" ? "http:" : "";
michael@0 532 var inner = Cc["@mozilla.org/network/io-service;1"].
michael@0 533 getService(Ci.nsIIOService).newURI(spec.replace(scheme, prefix),
michael@0 534 originalCharset, baseURI);
michael@0 535 var netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil);
michael@0 536 const URI_INHERITS_SECURITY_CONTEXT = Ci.nsIProtocolHandler
michael@0 537 .URI_INHERITS_SECURITY_CONTEXT;
michael@0 538 if (netutil.URIChainHasFlags(inner, URI_INHERITS_SECURITY_CONTEXT))
michael@0 539 throw Cr.NS_ERROR_MALFORMED_URI;
michael@0 540
michael@0 541 var uri = netutil.newSimpleNestedURI(inner);
michael@0 542 uri.spec = inner.spec.replace(prefix, scheme);
michael@0 543 return uri;
michael@0 544 },
michael@0 545
michael@0 546 newChannel: function GPH_newChannel(aUri) {
michael@0 547 var inner = aUri.QueryInterface(Ci.nsINestedURI).innerURI;
michael@0 548 var channel = Cc["@mozilla.org/network/io-service;1"].
michael@0 549 getService(Ci.nsIIOService).newChannelFromURI(inner, null);
michael@0 550 if (channel instanceof Components.interfaces.nsIHttpChannel)
michael@0 551 // Set this so we know this is supposed to be a feed
michael@0 552 channel.setRequestHeader("X-Moz-Is-Feed", "1", false);
michael@0 553 channel.originalURI = aUri;
michael@0 554 return channel;
michael@0 555 },
michael@0 556
michael@0 557 QueryInterface: function GPH_QueryInterface(iid) {
michael@0 558 if (iid.equals(Ci.nsIProtocolHandler) ||
michael@0 559 iid.equals(Ci.nsISupports))
michael@0 560 return this;
michael@0 561 throw Cr.NS_ERROR_NO_INTERFACE;
michael@0 562 }
michael@0 563 };
michael@0 564
michael@0 565 function FeedProtocolHandler() {
michael@0 566 this._init('feed');
michael@0 567 }
michael@0 568 FeedProtocolHandler.prototype = new GenericProtocolHandler();
michael@0 569 FeedProtocolHandler.prototype.classID = Components.ID("{4f91ef2e-57ba-472e-ab7a-b4999e42d6c0}");
michael@0 570
michael@0 571 function PodCastProtocolHandler() {
michael@0 572 this._init('pcast');
michael@0 573 }
michael@0 574 PodCastProtocolHandler.prototype = new GenericProtocolHandler();
michael@0 575 PodCastProtocolHandler.prototype.classID = Components.ID("{1c31ed79-accd-4b94-b517-06e0c81999d5}");
michael@0 576
michael@0 577 var components = [FeedConverter,
michael@0 578 FeedResultService,
michael@0 579 FeedProtocolHandler,
michael@0 580 PodCastProtocolHandler];
michael@0 581
michael@0 582
michael@0 583 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

mercurial