forked from mirrors/gecko-dev
This patch was autogenerated by my decomponents.py
It covers almost every file with the extension js, jsm, html, py,
xhtml, or xul.
It removes blank lines after removed lines, when the removed lines are
preceded by either blank lines or the start of a new block. The "start
of a new block" is defined fairly hackily: either the line starts with
//, ends with */, ends with {, <![CDATA[, """ or '''. The first two
cover comments, the third one covers JS, the fourth covers JS embedded
in XUL, and the final two cover JS embedded in Python. This also
applies if the removed line was the first line of the file.
It covers the pattern matching cases like "var {classes: Cc,
interfaces: Ci, utils: Cu, results: Cr} = Components;". It'll remove
the entire thing if they are all either Ci, Cr, Cc or Cu, or it will
remove the appropriate ones and leave the residue behind. If there's
only one behind, then it will turn it into a normal, non-pattern
matching variable definition. (For instance, "const { classes: Cc,
Constructor: CC, interfaces: Ci, utils: Cu } = Components" becomes
"const CC = Components.Constructor".)
MozReview-Commit-ID: DeSHcClQ7cG
--HG--
extra : rebase_source : d9c41878036c1ef7766ef5e91a7005025bc1d72b
459 lines
14 KiB
JavaScript
459 lines
14 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
ChromeUtils.import("resource://gre/modules/debug.js");
|
|
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
|
|
function LOG(str) {
|
|
dump("*** " + str + "\n");
|
|
}
|
|
|
|
const FS_CONTRACTID = "@mozilla.org/browser/feeds/result-service;1";
|
|
const FPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=feed";
|
|
const PCPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=pcast";
|
|
|
|
const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
|
|
const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
|
|
const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
|
|
const TYPE_ANY = "*/*";
|
|
|
|
const PREF_SELECTED_APP = "browser.feeds.handlers.application";
|
|
const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
|
|
const PREF_SELECTED_ACTION = "browser.feeds.handler";
|
|
const PREF_SELECTED_READER = "browser.feeds.handler.default";
|
|
|
|
const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
|
|
const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
|
|
const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
|
|
const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
|
|
|
|
const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
|
|
const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
|
|
const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
|
|
const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
|
|
|
|
function getPrefAppForType(t) {
|
|
switch (t) {
|
|
case Ci.nsIFeed.TYPE_VIDEO:
|
|
return PREF_VIDEO_SELECTED_APP;
|
|
|
|
case Ci.nsIFeed.TYPE_AUDIO:
|
|
return PREF_AUDIO_SELECTED_APP;
|
|
|
|
default:
|
|
return PREF_SELECTED_APP;
|
|
}
|
|
}
|
|
|
|
function getPrefWebForType(t) {
|
|
switch (t) {
|
|
case Ci.nsIFeed.TYPE_VIDEO:
|
|
return PREF_VIDEO_SELECTED_WEB;
|
|
|
|
case Ci.nsIFeed.TYPE_AUDIO:
|
|
return PREF_AUDIO_SELECTED_WEB;
|
|
|
|
default:
|
|
return PREF_SELECTED_WEB;
|
|
}
|
|
}
|
|
|
|
function getPrefActionForType(t) {
|
|
switch (t) {
|
|
case Ci.nsIFeed.TYPE_VIDEO:
|
|
return PREF_VIDEO_SELECTED_ACTION;
|
|
|
|
case Ci.nsIFeed.TYPE_AUDIO:
|
|
return PREF_AUDIO_SELECTED_ACTION;
|
|
|
|
default:
|
|
return PREF_SELECTED_ACTION;
|
|
}
|
|
}
|
|
|
|
function getPrefReaderForType(t) {
|
|
switch (t) {
|
|
case Ci.nsIFeed.TYPE_VIDEO:
|
|
return PREF_VIDEO_SELECTED_READER;
|
|
|
|
case Ci.nsIFeed.TYPE_AUDIO:
|
|
return PREF_AUDIO_SELECTED_READER;
|
|
|
|
default:
|
|
return PREF_SELECTED_READER;
|
|
}
|
|
}
|
|
|
|
function FeedConverter() {
|
|
}
|
|
FeedConverter.prototype = {
|
|
classID: Components.ID("{229fa115-9412-4d32-baf3-2fc407f76fb1}"),
|
|
|
|
/**
|
|
* This is the downloaded text data for the feed.
|
|
*/
|
|
_data: null,
|
|
|
|
/**
|
|
* This is the object listening to the conversion, which is ultimately the
|
|
* docshell for the load.
|
|
*/
|
|
_listener: null,
|
|
|
|
/**
|
|
* Records if the feed was sniffed
|
|
*/
|
|
_sniffed: false,
|
|
|
|
/**
|
|
* See nsIStreamConverter.idl
|
|
*/
|
|
convert(sourceStream, sourceType, destinationType,
|
|
context) {
|
|
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
},
|
|
|
|
/**
|
|
* See nsIStreamConverter.idl
|
|
*/
|
|
asyncConvertData(sourceType, destinationType,
|
|
listener, context) {
|
|
this._listener = listener;
|
|
},
|
|
|
|
/**
|
|
* Whether or not the preview page is being forced.
|
|
*/
|
|
_forcePreviewPage: false,
|
|
|
|
/**
|
|
* Release our references to various things once we're done using them.
|
|
*/
|
|
_releaseHandles() {
|
|
this._listener = null;
|
|
this._request = null;
|
|
this._processor = null;
|
|
},
|
|
|
|
/**
|
|
* See nsIFeedResultListener.idl
|
|
*/
|
|
handleResult(result) {
|
|
// Feeds come in various content types, which our feed sniffer coerces to
|
|
// the maybe.feed type. However, feeds are used as a transport for
|
|
// different data types, e.g. news/blogs (traditional feed), video/audio
|
|
// (podcasts) and photos (photocasts, photostreams). Each of these is
|
|
// different in that there's a different class of application suitable for
|
|
// handling feeds of that type, but without a content-type differentiation
|
|
// it is difficult for us to disambiguate.
|
|
//
|
|
// The other problem is that if the user specifies an auto-action handler
|
|
// for one feed application, the fact that the content type is shared means
|
|
// that all other applications will auto-load with that handler too,
|
|
// regardless of the content-type.
|
|
//
|
|
// This means that content-type alone is not enough to determine whether
|
|
// or not a feed should be auto-handled. This means that for feeds we need
|
|
// to always use this stream converter, even when an auto-action is
|
|
// specified, not the basic one provided by WebContentConverter. This
|
|
// converter needs to consume all of the data and parse it, and based on
|
|
// that determination make a judgment about type.
|
|
//
|
|
// Since there are no content types for this content, and I'm not going to
|
|
// invent any, the upshot is that while a user can set an auto-handler for
|
|
// generic feed content, the system will prevent them from setting an auto-
|
|
// handler for other stream types. In those cases, the user will always see
|
|
// the preview page and have to select a handler. We can guess and show
|
|
// a client handler, but will not be able to show web handlers for those
|
|
// types.
|
|
//
|
|
// If this is just a feed, not some kind of specialized application, then
|
|
// auto-handlers can be set and we should obey them.
|
|
try {
|
|
let feedService =
|
|
Cc["@mozilla.org/browser/feeds/result-service;1"].
|
|
getService(Ci.nsIFeedResultService);
|
|
if (!this._forcePreviewPage && result.doc) {
|
|
let feed = result.doc.QueryInterface(Ci.nsIFeed);
|
|
let handler = Services.prefs.getCharPref(getPrefActionForType(feed.type), "ask");
|
|
|
|
if (handler != "ask") {
|
|
if (handler == "reader")
|
|
handler = Services.prefs.getCharPref(getPrefReaderForType(feed.type), "bookmarks");
|
|
switch (handler) {
|
|
case "web":
|
|
let wccr =
|
|
Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
|
|
getService(Ci.nsIWebContentConverterService);
|
|
if ((feed.type == Ci.nsIFeed.TYPE_FEED &&
|
|
wccr.getAutoHandler(TYPE_MAYBE_FEED)) ||
|
|
(feed.type == Ci.nsIFeed.TYPE_VIDEO &&
|
|
wccr.getAutoHandler(TYPE_MAYBE_VIDEO_FEED)) ||
|
|
(feed.type == Ci.nsIFeed.TYPE_AUDIO &&
|
|
wccr.getAutoHandler(TYPE_MAYBE_AUDIO_FEED))) {
|
|
wccr.loadPreferredHandler(this._request);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
LOG("unexpected handler: " + handler);
|
|
// fall through -- let feed service handle error
|
|
case "bookmarks":
|
|
case "client":
|
|
case "default":
|
|
try {
|
|
let title = feed.title ? feed.title.plainText() : "";
|
|
let desc = feed.subtitle ? feed.subtitle.plainText() : "";
|
|
feedService.addToClientReader(result.uri.spec, title, desc, feed.type, handler);
|
|
return;
|
|
} catch (ex) { /* fallback to preview mode */ }
|
|
}
|
|
}
|
|
}
|
|
|
|
let chromeChannel;
|
|
|
|
// handling a redirect, hence forwarding the loadInfo from the old channel
|
|
// to the newchannel.
|
|
let oldChannel = this._request.QueryInterface(Ci.nsIChannel);
|
|
let loadInfo = oldChannel.loadInfo;
|
|
|
|
// If there was no automatic handler, or this was a podcast,
|
|
// photostream or some other kind of application, show the preview page
|
|
// if the parser returned a document.
|
|
if (result.doc) {
|
|
|
|
// Store the result in the result service so that the display
|
|
// page can access it.
|
|
feedService.addFeedResult(result);
|
|
|
|
// Now load the actual XUL document.
|
|
let aboutFeedsURI = Services.io.newURI("about:feeds");
|
|
chromeChannel = Services.io.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo);
|
|
chromeChannel.originalURI = result.uri;
|
|
|
|
// carry the origin attributes from the channel that loaded the feed.
|
|
chromeChannel.owner =
|
|
Services.scriptSecurityManager.createCodebasePrincipal(aboutFeedsURI,
|
|
loadInfo.originAttributes);
|
|
} else {
|
|
chromeChannel = Services.io.newChannelFromURIWithLoadInfo(result.uri, loadInfo);
|
|
}
|
|
|
|
chromeChannel.loadGroup = this._request.loadGroup;
|
|
chromeChannel.asyncOpen2(this._listener);
|
|
} finally {
|
|
this._releaseHandles();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* See nsIStreamListener.idl
|
|
*/
|
|
onDataAvailable(request, context, inputStream,
|
|
sourceOffset, count) {
|
|
if (this._processor)
|
|
this._processor.onDataAvailable(request, context, inputStream,
|
|
sourceOffset, count);
|
|
},
|
|
|
|
/**
|
|
* See nsIRequestObserver.idl
|
|
*/
|
|
onStartRequest(request, context) {
|
|
let channel = request.QueryInterface(Ci.nsIChannel);
|
|
|
|
// Check for a header that tells us there was no sniffing
|
|
// The value doesn't matter.
|
|
try {
|
|
let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
|
// Make sure to check requestSucceeded before the potentially-throwing
|
|
// getResponseHeader.
|
|
if (!httpChannel.requestSucceeded) {
|
|
// Just give up, but don't forget to cancel the channel first!
|
|
request.cancel(Cr.NS_BINDING_ABORTED);
|
|
return;
|
|
}
|
|
|
|
// Note: this throws if the header is not set.
|
|
httpChannel.getResponseHeader("X-Moz-Is-Feed");
|
|
} catch (ex) {
|
|
this._sniffed = true;
|
|
}
|
|
|
|
this._request = request;
|
|
|
|
// Save and reset the forced state bit early, in case there's some kind of
|
|
// error.
|
|
let feedService =
|
|
Cc["@mozilla.org/browser/feeds/result-service;1"].
|
|
getService(Ci.nsIFeedResultService);
|
|
this._forcePreviewPage = feedService.forcePreviewPage;
|
|
feedService.forcePreviewPage = false;
|
|
|
|
// Parse feed data as it comes in
|
|
this._processor =
|
|
Cc["@mozilla.org/feed-processor;1"].
|
|
createInstance(Ci.nsIFeedProcessor);
|
|
this._processor.listener = this;
|
|
this._processor.parseAsync(null, channel.URI);
|
|
|
|
this._processor.onStartRequest(request, context);
|
|
},
|
|
|
|
/**
|
|
* See nsIRequestObserver.idl
|
|
*/
|
|
onStopRequest(request, context, status) {
|
|
if (this._processor)
|
|
this._processor.onStopRequest(request, context, status);
|
|
},
|
|
|
|
/**
|
|
* See nsISupports.idl
|
|
*/
|
|
QueryInterface(iid) {
|
|
if (iid.equals(Ci.nsIFeedResultListener) ||
|
|
iid.equals(Ci.nsIStreamConverter) ||
|
|
iid.equals(Ci.nsIStreamListener) ||
|
|
iid.equals(Ci.nsIRequestObserver) ||
|
|
iid.equals(Ci.nsISupports))
|
|
return this;
|
|
throw Cr.NS_ERROR_NO_INTERFACE;
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Keeps parsed FeedResults around for use elsewhere in the UI after the stream
|
|
* converter completes.
|
|
*/
|
|
function FeedResultService() {
|
|
}
|
|
|
|
FeedResultService.prototype = {
|
|
classID: Components.ID("{2376201c-bbc6-472f-9b62-7548040a61c6}"),
|
|
|
|
/**
|
|
* A URI spec -> [nsIFeedResult] hash. We have to keep a list as the
|
|
* value in case the same URI is requested concurrently.
|
|
*/
|
|
_results: { },
|
|
|
|
/**
|
|
* See nsIFeedResultService.idl
|
|
*/
|
|
forcePreviewPage: false,
|
|
|
|
/**
|
|
* See nsIFeedResultService.idl
|
|
*/
|
|
addToClientReader(spec, title, subtitle, feedType, feedReader) {
|
|
if (!feedReader) {
|
|
feedReader = "default";
|
|
}
|
|
|
|
let handler = Services.prefs.getCharPref(getPrefActionForType(feedType), "bookmarks");
|
|
if (handler == "ask" || handler == "reader")
|
|
handler = feedReader;
|
|
|
|
switch (handler) {
|
|
case "client":
|
|
Services.cpmm.sendAsyncMessage("FeedConverter:ExecuteClientApp",
|
|
{ spec,
|
|
title,
|
|
subtitle,
|
|
feedHandler: getPrefAppForType(feedType) });
|
|
break;
|
|
case "default":
|
|
// Default system feed reader
|
|
Services.cpmm.sendAsyncMessage("FeedConverter:ExecuteClientApp",
|
|
{ spec,
|
|
title,
|
|
subtitle,
|
|
feedHandler: "default" });
|
|
break;
|
|
default:
|
|
// "web" should have been handled elsewhere
|
|
LOG("unexpected handler: " + handler);
|
|
// fall through
|
|
case "bookmarks":
|
|
Services.cpmm.sendAsyncMessage("FeedConverter:addLiveBookmark",
|
|
{ spec, title, subtitle });
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* See nsIFeedResultService.idl
|
|
*/
|
|
addFeedResult(feedResult) {
|
|
NS_ASSERT(feedResult.uri != null, "null URI!");
|
|
NS_ASSERT(feedResult.uri != null, "null feedResult!");
|
|
let spec = feedResult.uri.spec;
|
|
if (!this._results[spec])
|
|
this._results[spec] = [];
|
|
this._results[spec].push(feedResult);
|
|
},
|
|
|
|
/**
|
|
* See nsIFeedResultService.idl
|
|
*/
|
|
getFeedResult(uri) {
|
|
NS_ASSERT(uri != null, "null URI!");
|
|
let resultList = this._results[uri.spec];
|
|
for (let result of resultList) {
|
|
if (result.uri == uri)
|
|
return result;
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* See nsIFeedResultService.idl
|
|
*/
|
|
removeFeedResult(uri) {
|
|
NS_ASSERT(uri != null, "null URI!");
|
|
let resultList = this._results[uri.spec];
|
|
if (!resultList)
|
|
return;
|
|
let deletions = 0;
|
|
for (let i = 0; i < resultList.length; ++i) {
|
|
if (resultList[i].uri == uri) {
|
|
delete resultList[i];
|
|
++deletions;
|
|
}
|
|
}
|
|
|
|
// send the holes to the end
|
|
resultList.sort();
|
|
// and trim the list
|
|
resultList.splice(resultList.length - deletions, deletions);
|
|
if (resultList.length == 0)
|
|
delete this._results[uri.spec];
|
|
},
|
|
|
|
createInstance(outer, iid) {
|
|
if (outer != null)
|
|
throw Cr.NS_ERROR_NO_AGGREGATION;
|
|
return this.QueryInterface(iid);
|
|
},
|
|
|
|
QueryInterface(iid) {
|
|
if (iid.equals(Ci.nsIFeedResultService) ||
|
|
iid.equals(Ci.nsIFactory) ||
|
|
iid.equals(Ci.nsISupports))
|
|
return this;
|
|
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
|
},
|
|
};
|
|
|
|
|
|
var components = [FeedConverter,
|
|
FeedResultService];
|
|
|
|
|
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
|