Bug 303645. Add support for media feeds and objects (enclosures). Patch by Will Guaraldi <will.guaraldi@pculture.org>. r=sayrer/gavin, ui-r=beltzner, a=beltzner

This commit is contained in:
sayrer@gmail.com 2008-01-29 18:08:57 -08:00
Родитель 96076d19b0
Коммит af4a306e7c
32 изменённых файлов: 691 добавлений и 5 удалений

Просмотреть файл

@ -24,6 +24,7 @@
# Asaf Romano <mano@mozilla.com>
# Robert Sayre <sayrer@gmail.com>
# Michael Ventnor <m.ventnor@gmail.com>
# Will Guaraldi <will.guaraldi@pculture.org>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
@ -91,8 +92,35 @@ const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";
const TITLE_ID = "feedTitleText";
const SUBTITLE_ID = "feedSubtitleText";
/**
* Converts a number of bytes to the appropriate unit that results in a
* number that needs fewer than 4 digits
*
* @return a pair: [new value with 3 sig. figs., its unit]
*/
function convertByteUnits(aBytes) {
var units = ["bytes", "kilobyte", "megabyte", "gigabyte"];
let unitIndex = 0;
// convert to next unit if it needs 4 digits (after rounding), but only if
// we know the name of the next unit
while ((aBytes >= 999.5) && (unitIndex < units.length - 1)) {
aBytes /= 1024;
unitIndex++;
}
// Get rid of insignificant bits by truncating to 1 or 0 decimal points
// 0 -> 0; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235
aBytes = aBytes.toFixed((aBytes > 0) && (aBytes < 100) ? 1 : 0);
return [aBytes, units[unitIndex]];
}
function FeedWriter() {}
FeedWriter.prototype = {
_mimeSvc : Cc["@mozilla.org/mime;1"].
getService(Ci.nsIMIMEService),
_getPropertyAsBag: function FW__getPropertyAsBag(container, property) {
return container.fields.getProperty(property).
QueryInterface(Ci.nsIPropertyBag2);
@ -357,6 +385,12 @@ FeedWriter.prototype = {
}
body.className = "feedEntryContent";
entryContainer.appendChild(body);
if (entry.enclosures && entry.enclosures.length > 0) {
var enclosuresDiv = this._buildEnclosureDiv(entry);
entryContainer.appendChild(enclosuresDiv);
}
feedContent.appendChild(entryContainer);
var clearDiv = this._document.createElementNS(HTML_NS, "div");
clearDiv.style.clear = "both";
@ -364,6 +398,105 @@ FeedWriter.prototype = {
}
},
/**
* Takes a url to a media item and returns the best name it can come up with.
* Frequently this is the filename portion (e.g. passing in
* http://example.com/foo.mpeg would return "foo.mpeg"), but in more complex
* cases, this will return the entire url (e.g. passing in
* http://example.com/somedirectory/ would return
* http://example.com/somedirectory/).
* @param aURL
* The URL string from which to create a display name
* @returns a string
*/
_getURLDisplayName: function FW__getURLDisplayName(aURL) {
var url = makeURI(aURL);
url.QueryInterface(Ci.nsIURL);
if (url == null || url.fileName.length == 0)
return aURL;
return decodeURI(url.fileName);
},
/**
* Takes a FeedEntry with enclosures, generates the HTML code to represent
* them, and returns that.
* @param entry
* FeedEntry with enclosures
* @returns element
*/
_buildEnclosureDiv: function FW__buildEnclosureDiv(entry) {
var enclosuresDiv = this._document.createElementNS(HTML_NS, "div");
enclosuresDiv.setAttribute("class", "enclosures");
enclosuresDiv.appendChild(this._document.createTextNode(this._getString("mediaLabel")));
var roundme = function(n) {
return (Math.round(n * 100) / 100).toLocaleString();
}
for (var i_enc = 0; i_enc < entry.enclosures.length; ++i_enc) {
var enc = entry.enclosures.queryElementAt(i_enc, Ci.nsIWritablePropertyBag2);
if (!(enc.hasKey("url")))
continue;
var enclosureDiv = this._document.createElementNS(HTML_NS, "div");
enclosureDiv.setAttribute("class", "enclosure");
var mozicon = "moz-icon://.txt?size=16";
var type_text = null;
var size_text = null;
if (enc.hasKey("type")) {
type_text = enc.get("type");
try {
var handlerInfoWrapper = this._mimeSvc.getFromTypeAndExtension(enc.get("type"), null);
if (handlerInfoWrapper)
type_text = handlerInfoWrapper.description;
if (type_text && type_text.length > 0)
mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type");
} catch (ex) { }
}
if (enc.hasKey("length") && /^[0-9]+$/.test(enc.get("length"))) {
var enc_size = convertByteUnits(parseInt(enc.get("length")));
var size_text = this._getFormattedString("enclosureSizeText",
[enc_size[0], this._getString(enc_size[1])]);
}
var iconimg = this._document.createElementNS(HTML_NS, "img");
iconimg.setAttribute("src", mozicon);
iconimg.setAttribute("class", "type-icon");
enclosureDiv.appendChild(iconimg);
enclosureDiv.appendChild(this._document.createTextNode( " " ));
var enc_href = this._document.createElementNS(HTML_NS, "a");
enc_href.appendChild(this._document.createTextNode(this._getURLDisplayName(enc.get("url"))));
this._safeSetURIAttribute(enc_href, "href", enc.get("url"));
enclosureDiv.appendChild(enc_href);
if (type_text && size_text)
enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ", " + size_text + ")"));
else if (type_text)
enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ")"))
else if (size_text)
enclosureDiv.appendChild(this._document.createTextNode( " (" + size_text + ")"))
enclosuresDiv.appendChild(enclosureDiv);
}
return enclosuresDiv;
},
/**
* Gets a valid nsIFeedContainer object from the parsed nsIFeedResult.
* Displays error information if there was one.

Двоичные данные
browser/themes/gnomestripe/browser/feeds/audioFeedIcon.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.8 KiB

Двоичные данные
browser/themes/gnomestripe/browser/feeds/audioFeedIcon16.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 830 B

Просмотреть файл

@ -152,3 +152,21 @@ a[href] img {
font-size: 85%;
font-weight: normal;
}
.type-icon {
vertical-align: bottom;
height: 16px;
width: 16px;
}
.enclosures {
border: 1px solid THreeDShadow;
padding: 1em;
margin: 1em auto;
background: -moz-Dialog;
}
.enclosure {
vertical-align: middle;
margin-left: 2px;
}

Двоичные данные
browser/themes/gnomestripe/browser/feeds/videoFeedIcon.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.8 KiB

Двоичные данные
browser/themes/gnomestripe/browser/feeds/videoFeedIcon16.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 830 B

Просмотреть файл

@ -26,6 +26,10 @@ classic.jar:
skin/classic/browser/Toolbar-small.png
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
skin/classic/browser/feeds/videoFeedIcon.png (feeds/videoFeedIcon.png)
skin/classic/browser/feeds/videoFeedIcon16.png (feeds/videoFeedIcon16.png)
skin/classic/browser/feeds/audioFeedIcon.png (feeds/audioFeedIcon.png)
skin/classic/browser/feeds/audioFeedIcon16.png (feeds/audioFeedIcon16.png)
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
* skin/classic/browser/places/bookmarkProperties.css (places/bookmarkProperties.css)
skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png)

Двоичные данные
browser/themes/pinstripe/browser/feeds/audioFeedIcon.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.7 KiB

Двоичные данные
browser/themes/pinstripe/browser/feeds/audioFeedIcon16.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.5 KiB

Просмотреть файл

@ -126,3 +126,21 @@ a[href] img {
font-size: 85%;
font-weight: normal;
}
.type-icon {
vertical-align: bottom;
height: 16px;
width: 16px;
}
.enclosures {
border: 1px solid THreeDShadow;
padding: 1em;
margin: 1em auto;
background: -moz-Dialog;
}
.enclosure {
vertical-align: middle;
margin-left: 2px;
}

Двоичные данные
browser/themes/pinstripe/browser/feeds/videoFeedIcon.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.7 KiB

Двоичные данные
browser/themes/pinstripe/browser/feeds/videoFeedIcon16.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.5 KiB

Просмотреть файл

@ -32,6 +32,10 @@ classic.jar:
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
skin/classic/browser/feeds/videoFeedIcon.png (feeds/videoFeedIcon.png)
skin/classic/browser/feeds/videoFeedIcon16.png (feeds/videoFeedIcon16.png)
skin/classic/browser/feeds/audioFeedIcon.png (feeds/audioFeedIcon.png)
skin/classic/browser/feeds/audioFeedIcon16.png (feeds/audioFeedIcon16.png)
skin/classic/browser/radio-selected-bg.gif
skin/classic/browser/setDesktopBackground.css
skin/classic/browser/monitor.png

Двоичные данные
browser/themes/winstripe/browser/feeds/audioFeedIcon.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.7 KiB

Двоичные данные
browser/themes/winstripe/browser/feeds/audioFeedIcon16.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.5 KiB

Просмотреть файл

@ -152,3 +152,21 @@ a[href] img {
font-size: 85%;
font-weight: normal;
}
.type-icon {
vertical-align: bottom;
height: 16px;
width: 16px;
}
.enclosures {
border: 1px solid THreeDShadow;
padding: 1em;
margin: 1em auto;
background: -moz-Dialog;
}
.enclosure {
vertical-align: middle;
margin-left: 2px;
}

Двоичные данные
browser/themes/winstripe/browser/feeds/videoFeedIcon.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.7 KiB

Двоичные данные
browser/themes/winstripe/browser/feeds/videoFeedIcon16.png Executable file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 3.5 KiB

Просмотреть файл

@ -31,6 +31,10 @@ classic.jar:
skin/classic/browser/wrench.png
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
skin/classic/browser/feeds/videoFeedIcon.png (feeds/videoFeedIcon.png)
skin/classic/browser/feeds/videoFeedIcon16.png (feeds/videoFeedIcon16.png)
skin/classic/browser/feeds/audioFeedIcon.png (feeds/audioFeedIcon.png)
skin/classic/browser/feeds/audioFeedIcon16.png (feeds/audioFeedIcon16.png)
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/places/places.css (places/places.css)
skin/classic/browser/places/organizer.css (places/organizer.css)

Просмотреть файл

@ -22,6 +22,7 @@
* Ben Goodger <beng@google.com>
* Myk Melez <myk@mozilla.org>
* Michael Ventnor <m.ventnor@gmail.com>
* Will Guaraldi <will.guaraldi@pculture.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -220,7 +221,9 @@ var gNamespaces = {
"http://my.netscape.com/rdf/simple/0.9/":"rss1",
"http://wellformedweb.org/CommentAPI/":"wfw",
"http://purl.org/rss/1.0/modules/wiki/":"wiki",
"http://www.w3.org/XML/1998/namespace":"xml"
"http://www.w3.org/XML/1998/namespace":"xml",
"http://search.yahoo.com/mrss/":"media",
"http://search.yahoo.com/mrss":"media"
}
// We allow a very small set of namespaces in XHTML content,
@ -264,6 +267,8 @@ function Feed() {
this.authors = Cc[ARRAY_CONTRACTID].createInstance(Ci.nsIMutableArray);
this.contributors = Cc[ARRAY_CONTRACTID].createInstance(Ci.nsIMutableArray);
this.baseURI = null;
this.enclosureCount = 0;
this.type = Ci.nsIFeed.TYPE_FEED;
}
Feed.prototype = {
@ -303,6 +308,8 @@ Feed.prototype = {
if (bagHasKey(this.fields, "links"))
this._atomLinksToURI();
this._calcEnclosureCountAndFeedType();
// Resolve relative image links
if (this.image && bagHasKey(this.image, "url"))
this._resolveImageLink();
@ -311,6 +318,67 @@ Feed.prototype = {
this.searchLists.title]);
},
_calcEnclosureCountAndFeedType: function Feed_calcEnclosureCountAndFeedType() {
var entries_with_enclosures = 0;
var audio_count = 0;
var image_count = 0;
var video_count = 0;
var other_count = 0;
for (var i = 0; i < this.items.length; ++i) {
var entry = this.items.queryElementAt(i, Ci.nsIFeedEntry);
entry.QueryInterface(Ci.nsIFeedContainer);
if (entry.enclosures && entry.enclosures.length > 0) {
++entries_with_enclosures;
for (var e = 0; e < entry.enclosures.length; ++e) {
var enc = entry.enclosures.queryElementAt(e, Ci.nsIWritablePropertyBag2);
if (enc.hasKey("type")) {
var enctype = enc.get("type");
if (/^audio/.test(enctype)) {
++audio_count;
} else if (/^image/.test(enctype)) {
++image_count;
} else if (/^video/.test(enctype)) {
++video_count;
} else {
++other_count;
}
} else {
++other_count;
}
}
}
}
var feedtype = Ci.nsIFeed.TYPE_FEED;
// For a feed to be marked as TYPE_VIDEO, TYPE_AUDIO and TYPE_IMAGE,
// we enforce two things:
//
// 1. all entries must have at least one enclosure
// 2. all enclosures must be video for TYPE_VIDEO, audio for TYPE_AUDIO or image
// for TYPE_IMAGE
//
// Otherwise it's a TYPE_FEED.
if (entries_with_enclosures == this.items.length && other_count == 0) {
if (audio_count > 0 && !video_count && !image_count) {
feedtype = Ci.nsIFeed.TYPE_AUDIO;
} else if (image_count > 0 && !audio_count && !video_count) {
feedtype = Ci.nsIFeed.TYPE_IMAGE;
} else if (video_count > 0 && !audio_count && !image_count) {
feedtype = Ci.nsIFeed.TYPE_VIDEO;
}
}
this.type = feedtype;
this.enclosureCount = other_count + video_count + audio_count + image_count;
},
_atomLinksToURI: function Feed_linkToURI() {
var links = this.fields.getPropertyAsInterface("links", Ci.nsIArray);
var alternates = findAtomLinks("alternate", links);
@ -407,7 +475,10 @@ Entry.prototype = {
// Assign Atom link if needed
if (bagHasKey(this.fields, "links"))
this._atomLinksToURI();
// Populate enclosures array
this._populateEnclosures();
// The link might be a guid w/ permalink=true
if (!this.link && bagHasKey(this.fields, "guid")) {
var guid = this.fields.getProperty("guid");
@ -430,6 +501,133 @@ Entry.prototype = {
this.searchLists.title]);
},
_populateEnclosures: function Entry_populateEnclosures() {
if (bagHasKey(this.fields, "links"))
this._atomLinksToEnclosures();
// Add RSS2 enclosure to enclosures
if (bagHasKey(this.fields, "enclosure"))
this._enclosureToEnclosures();
// Add media:content to enclosures
if (bagHasKey(this.fields, "mediacontent"))
this._mediacontentToEnclosures();
// Add media:content in media:group to enclosures
if (bagHasKey(this.fields, "mediagroup"))
this._mediagroupToEnclosures();
},
__enclosure_map: null,
_addToEnclosures: function Entry_addToEnclosures(new_enc) {
if (!bagHasKey(new_enc, "url"))
return;
if (this.__enclosure_map == null)
this.__enclosure_map = {};
var previous_enc = this.__enclosure_map[new_enc.getPropertyAsAString("url")];
if (previous_enc != undefined) {
previous_enc.QueryInterface(Ci.nsIWritablePropertyBag2);
if (!bagHasKey(previous_enc, "type") && bagHasKey(new_enc, "type"))
previous_enc.setPropertyAsAString("type", new_enc.getPropertyAsAString("type"));
if (!bagHasKey(previous_enc, "length") && bagHasKey(new_enc, "length"))
previous_enc.setPropertyAsAString("length", new_enc.getPropertyAsAString("length"));
return;
}
if (this.enclosures == null) {
this.enclosures = Cc[ARRAY_CONTRACTID].createInstance(Ci.nsIMutableArray);
this.enclosures.QueryInterface(Ci.nsIMutableArray);
}
this.enclosures.appendElement(new_enc, false);
this.__enclosure_map[new_enc.getPropertyAsAString("url")] = new_enc;
},
_atomLinksToEnclosures: function Entry_linkToEnclosure() {
var links = this.fields.getPropertyAsInterface("links", Ci.nsIArray);
var enc_links = findAtomLinks("enclosure", links);
if (enc_links.length == 0)
return;
for (var i = 0; i < enc_links.length; ++i) {
var link = enc_links[i];
// an enclosure must have an href
if (!(link.getProperty("href")))
return;
var enc = Cc[BAG_CONTRACTID].createInstance(Ci.nsIWritablePropertyBag2);
// copy Atom bits over to equivalent enclosure bits
enc.setPropertyAsAString("url", link.getPropertyAsAString("href"));
if (bagHasKey(link, "type"))
enc.setPropertyAsAString("type", link.getPropertyAsAString("type"));
if (bagHasKey(link, "length"))
enc.setPropertyAsAString("length", link.getPropertyAsAString("length"));
this._addToEnclosures(enc);
}
},
_enclosureToEnclosures: function Entry_enclosureToEnclosures() {
var enc = this.fields.getPropertyAsInterface("enclosure", Ci.nsIPropertyBag2);
if (!(enc.getProperty("url")))
return;
this._addToEnclosures(enc);
},
_mediacontentToEnclosures: function Entry_mediacontentToEnclosures() {
var mc = this.fields.getPropertyAsInterface("mediacontent", Ci.nsIPropertyBag2);
if (!(mc.getProperty("url")))
return;
var enc = Cc[BAG_CONTRACTID].createInstance(Ci.nsIWritablePropertyBag2);
enc.setPropertyAsAString("url", mc.getPropertyAsAString("url"));
if (bagHasKey(mc, "fileSize"))
enc.setPropertyAsAString("length", mc.getPropertyAsAString("fileSize"));
if (bagHasKey(mc, "type"))
enc.setPropertyAsAString("type", mc.getPropertyAsAString("type"));
this._addToEnclosures(enc);
},
_mediagroupToEnclosures: function Entry_mediagroupToEnclosures() {
var group = this.fields.getPropertyAsInterface("mediagroup", Ci.nsIPropertyBag2);
var content = group.getPropertyAsInterface("mediacontent", Ci.nsIArray);
for (var i = 0; i < content.length; ++i) {
var contentElement = content.queryElementAt(i, Ci.nsIWritablePropertyBag2);
// media:content don't require url, but if it's not there, we should
// skip it.
if (!bagHasKey(contentElement, "url"))
continue;
var enc = Cc[BAG_CONTRACTID].createInstance(Ci.nsIWritablePropertyBag2);
// copy media:content bits over to equivalent enclosure bits
enc.setPropertyAsAString("url", contentElement.getPropertyAsAString("url"));
if (bagHasKey(contentElement, "type")) {
enc.setPropertyAsAString("type", contentElement.getPropertyAsAString("type"));
}
if (bagHasKey(contentElement, "fileSize")) {
enc.setPropertyAsAString("length", contentElement.getPropertyAsAString("fileSize"));
}
this._addToEnclosures(enc);
}
},
// XPCOM stuff
classDescription: ENTRY_CLASSNAME,
classID: ENTRY_CLASSID,
@ -1063,7 +1261,9 @@ function FeedProcessor() {
"dc:contributor": new ElementInfo("contributors", Cc[PERSON_CONTRACTID],
rssAuthor, true),
"category": new ElementInfo("categories", null, rssCatTerm, true),
"enclosure": new ElementInfo("enclosure", null, null, true),
"enclosure": new ElementInfo("enclosure", null, null, false),
"media:content": new ElementInfo("mediacontent", null, null, false),
"media:group": new ElementInfo("mediagroup", null, null, false),
"guid": new ElementInfo("guid", null, rssGuid, false)
},
@ -1075,6 +1275,10 @@ function FeedProcessor() {
"hour": new ElementInfo("hours", null, rssArrayElement, true)
},
"IN_MEDIAGROUP": {
"media:content": new ElementInfo("mediacontent", null, null, true)
},
/********* RSS1 **********/
"IN_RDF": {
// If we hit a rss1:channel, we can verify that we have RSS1

Просмотреть файл

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: item enclosure works
Expect: var links = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).fields.getProperty('links'); links.QueryInterface(Components.interfaces.nsIArray); var link = links.queryElementAt(0, Components.interfaces.nsIPropertyBag2); ((link.getProperty('length') == '24986239') && (link.getProperty('type') == 'audio/mpeg') && (link.getProperty('href') == 'http://dallas.example.com/joebob_050689.mp3') && (feed.type == 1) && (feed.enclosureCount == 1));
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<id>tag:snellspace.com,2006:/atom/conformance/linktest/</id>
<title>Atom Link Tests</title>
<updated>2005-01-18T15:10:00Z</updated>
<author><name>James Snell</name></author>
<link href="http://www.intertwingly.net/wiki/pie/LinkConformanceTests" />
<link rel="self" href="http://www.snellspace.com/public/linktests.xml" />
<entry>
<id>tag:snellspace.com,2006:/atom/conformance/linktest/4</id>
<title>One of each core link rel type + An additional alternate link</title>
<updated>2005-01-18T15:00:04Z</updated>
<summary>The aggregator should pick either the first or last links as the alternate. First link is likely better.</summary>
<link rel="enclosure" length="24986239" type="audio/mpeg" href="http://dallas.example.com/joebob_050689.mp3" />
</entry>
</feed>

Просмотреть файл

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: item enclosure added to enclosures
Expect: var encs = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).enclosures; encs.QueryInterface(Components.interfaces.nsIArray); var enc = encs.queryElementAt(0, Components.interfaces.nsIPropertyBag2); ((enc.getProperty('length') == '24986239') && (enc.getProperty('type') == 'audio/mpeg') && (enc.getProperty('url') == 'http://dallas.example.com/joebob_050689.mp3'));
-->
<feed xmlns="http://www.w3.org/2005/Atom">
<id>tag:snellspace.com,2006:/atom/conformance/linktest/</id>
<title>Atom Link Tests</title>
<updated>2005-01-18T15:10:00Z</updated>
<author><name>James Snell</name></author>
<link href="http://www.intertwingly.net/wiki/pie/LinkConformanceTests" />
<link rel="self" href="http://www.snellspace.com/public/linktests.xml" />
<entry>
<id>tag:snellspace.com,2006:/atom/conformance/linktest/4</id>
<title>One of each core link rel type + An additional alternate link</title>
<updated>2005-01-18T15:00:04Z</updated>
<summary>The aggregator should pick either the first or last links as the alternate. First link is likely better.</summary>
<link rel="enclosure" length="24986239" type="audio/mpeg" href="http://dallas.example.com/joebob_050689.mp3" />
</entry>
</feed>

Просмотреть файл

@ -2,7 +2,7 @@
<!--
Description: item enclosure works
Expect: var encs = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).fields.getProperty('enclosure'); encs.QueryInterface(Components.interfaces.nsIArray); var enc = encs.queryElementAt(0, Components.interfaces.nsIPropertyBag); ((enc.getProperty('length') == '24986239') && (enc.getProperty('type') == 'audio/mpeg') && (enc.getProperty('url') == 'http://dallas.example.com/joebob_050689.mp3'));
Expect: var enc = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).fields.getProperty('enclosure'); enc.QueryInterface(Components.interfaces.nsIPropertyBag); ((enc.getProperty('length') == '24986239') && (enc.getProperty('type') == 'audio/mpeg') && (enc.getProperty('url') == 'http://dallas.example.com/joebob_050689.mp3') && (feed.type == 1) && (feed.enclosureCount == 1));
-->
<rss version="2.0" >
@ -18,4 +18,4 @@ Expect: var encs = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEnt
<description>I'm headed for France. I wasn't gonna go this year, but then last week &lt;a href="http://www.imdb.com/title/tt0086525/"&gt;Valley Girl&lt;/a&gt; came out and I said to myself, Joe Bob, you gotta get out of the country for a while.</description></item>
</channel>
</rss>
</rss>

Просмотреть файл

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: feed with duplicate enclosures on a single item
Expect: ((feed.type == 4) && (feed.enclosureCount == 1));
-->
<rss xmlns:media="http://search.yahoo.com/mrss" version="2.0" >
<channel>
<item>
<enclosure length="24986239" type="video/mpeg" url="http://dallas.example.com/joebob_050689.mpeg" />
<media:content fileSize="24986239" type="video/mpeg" url="http://dallas.example.com/joebob_050689.mpeg" />
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>test audio</title>
<category domain="foo">bar</category>
<description>Listen to the words that are coming out of my mouth.</description>
</item>
</channel>
</rss>

Просмотреть файл

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: feed with duplicate enclosures on a single item with different data available
Expect: var enc = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).fields.getProperty('enclosure'); enc.QueryInterface(Components.interfaces.nsIPropertyBag); ((enc.getProperty('length') == '24986239') && (enc.getProperty('type') == 'video/mpeg') && (feed.type == 4) && (feed.enclosureCount == 1) );
-->
<rss xmlns:media="http://search.yahoo.com/mrss" version="2.0" >
<channel>
<item>
<enclosure url="http://dallas.example.com/joebob_050689.mpeg" />
<media:content fileSize="24986239" type="video/mpeg" url="http://dallas.example.com/joebob_050689.mpeg" />
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>test audio</title>
<category domain="foo">bar</category>
<description>Listen to the words that are coming out of my mouth.</description>
</item>
</channel>
</rss>

Просмотреть файл

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: feed with different kinds of enclosures that should be TYPE_FEED (0)
Expect: ((feed.type == 0) && (feed.enclosureCount == 2));
-->
<rss version="2.0" >
<channel>
<item>
<enclosure length="24986239" type="audio/mpeg" url="http://dallas.example.com/joebob_050689.mp3" />
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>test audio</title>
<category domain="foo">bar</category>
<description>Listen to the words that are coming out of my mouth.</description>
</item>
<item>
<enclosure length="3000000" type="video/mpeg" url="http://dallas.example.com/joebob_pants.mpeg" />
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>test video</title>
<category domain="foo">bar</category>
<description>Look into my eyes....</description>
</item>
</channel>
</rss>

Просмотреть файл

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: feed that doesn't have at least one enclosure per entry should be TYPE_FEED (0)
Expect: ((feed.type == 0) && (feed.enclosureCount == 1));
-->
<rss version="2.0" >
<channel>
<item>
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>no video this week!</title>
<category domain="foo">bar</category>
<description>I'm on a trip to the moon this week for this year's Spaceshot Vlogger
conference. No video this week!</description>
</item>
<item>
<enclosure length="3000000" type="video/mpeg" url="http://dallas.example.com/joebob_pants.mpeg" />
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>orange keyboard</title>
<category domain="foo">bar</category>
<description>Crazy things happen when you paint your keyboard orange.</description>
</item>
</channel>
</rss>

Просмотреть файл

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: item enclosure is added to enclosures array
Expect: var encs = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).enclosures; encs.QueryInterface(Components.interfaces.nsIArray); var enc = encs.queryElementAt(0, Components.interfaces.nsIPropertyBag2); ((enc.getProperty('length') == '24986239') && (enc.getProperty('type') == 'audio/mpeg') && (enc.getProperty('url') == 'http://dallas.example.com/joebob_050689.mp3'));
-->
<rss version="2.0" >
<channel>
<item>
<enclosure length="24986239" type="audio/mpeg" url="http://dallas.example.com/joebob_050689.mp3" />
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>test</title>
<category domain="foo">bar</category>
<description>I'm headed for France. I wasn't gonna go this year, but then last week &lt;a href="http://www.imdb.com/title/tt0086525/"&gt;Valley Girl&lt;/a&gt; came out and I said to myself, Joe Bob, you gotta get out of the country for a while.</description></item>
</channel>
</rss>

Просмотреть файл

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: mrss content works
Expect: var enc = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).fields.getProperty('mediacontent'); enc.QueryInterface(Components.interfaces.nsIPropertyBag); ((enc.getProperty('fileSize') == '24986239') && (enc.getProperty('type') == 'video/mpeg') && (enc.getProperty('url') == 'http://dallas.example.com/joebob_050689.mpeg') && (feed.type == 4) && (feed.enclosureCount == 1));
-->
<rss xmlns:media="http://search.yahoo.com/mrss" version="2.0" >
<channel>
<item>
<media:content fileSize="24986239" type="video/mpeg" url="http://dallas.example.com/joebob_050689.mpeg" />
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>test</title>
<category domain="foo">bar</category>
<description>I'm headed for France. I wasn't gonna go this year, but then last week &lt;a href="http://www.imdb.com/title/tt0086525/"&gt;Valley Girl&lt;/a&gt; came out and I said to myself, Joe Bob, you gotta get out of the country for a while.</description></item>
</channel>
</rss>

Просмотреть файл

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: mrss content added to enclosures array
Expect: var encs = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).enclosures; encs.QueryInterface(Components.interfaces.nsIArray); var enc = encs.queryElementAt(0, Components.interfaces.nsIPropertyBag); ((enc.getProperty('length') == '24986239') && (enc.getProperty('type') == 'audio/mpeg') && (enc.getProperty('url') == 'http://dallas.example.com/joebob_050689.mp3') && (feed.type == 1) && (feed.enclosureCount == 1));
-->
<rss xmlns:media="http://search.yahoo.com/mrss" version="2.0" >
<channel>
<item>
<media:content fileSize="24986239" type="audio/mpeg" url="http://dallas.example.com/joebob_050689.mp3" />
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>test</title>
<category domain="foo">bar</category>
<description>I'm headed for France. I wasn't gonna go this year, but then last week &lt;a href="http://www.imdb.com/title/tt0086525/"&gt;Valley Girl&lt;/a&gt; came out and I said to myself, Joe Bob, you gotta get out of the country for a while.</description></item>
</channel>
</rss>

Просмотреть файл

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: mrss group content works
Expect: var mg = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).fields.getProperty('mediagroup'); mg.QueryInterface(Components.interfaces.nsIPropertyBag); var mcs = mg.getProperty("mediacontent"); mcs.QueryInterface(Components.interfaces.nsIArray); var mc1 = mcs.queryElementAt(0, Components.interfaces.nsIPropertyBag); var mc2 = mcs.queryElementAt(1, Components.interfaces.nsIPropertyBag); ((mc1.getProperty('fileSize') == '400') && (mc1.getProperty('type') == 'audio/mpeg') && (mc1.getProperty('url') == 'http://dallas.example.com/joebob_050689_2.mp3') && (mc2.getProperty('fileSize') == '200') && (mc2.getProperty('type') == 'audio/mpeg') && (mc2.getProperty('url') == 'http://dallas.example.com/joebob_050689_1.mp3'));
-->
<rss xmlns:media="http://search.yahoo.com/mrss" version="2.0" >
<channel>
<item>
<media:group>
<media:content fileSize="400" type="audio/mpeg" url="http://dallas.example.com/joebob_050689_2.mp3" />
<media:content fileSize="200" type="audio/mpeg" url="http://dallas.example.com/joebob_050689_1.mp3" />
</media:group>
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>test</title>
<category domain="foo">bar</category>
<description>I'm headed for France. I wasn't gonna go this year, but then last week &lt;a href="http://www.imdb.com/title/tt0086525/"&gt;Valley Girl&lt;/a&gt; came out and I said to myself, Joe Bob, you gotta get out of the country for a while.</description></item>
</channel>
</rss>

Просмотреть файл

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!--
Description: mrss group content works
Expect: var encs = feed.items.queryElementAt(0, Components.interfaces.nsIFeedEntry).enclosures; encs.QueryInterface(Components.interfaces.nsIArray); var enc1 = encs.queryElementAt(0, Components.interfaces.nsIPropertyBag); var enc2 = encs.queryElementAt(1, Components.interfaces.nsIPropertyBag); ((enc1.getProperty('length') == '400') && (enc1.getProperty('type') == 'audio/mpeg') && (enc1.getProperty('url') == 'http://dallas.example.com/joebob_050689_2.mp3') && (enc2.getProperty('length') == '200') && (enc2.getProperty('type') == 'audio/mpeg') && (enc2.getProperty('url') == 'http://dallas.example.com/joebob_050689_1.mp3'));
-->
<rss xmlns:media="http://search.yahoo.com/mrss" version="2.0" >
<channel>
<item>
<media:group>
<media:content fileSize="400" type="audio/mpeg" url="http://dallas.example.com/joebob_050689_2.mp3" />
<media:content fileSize="200" type="audio/mpeg" url="http://dallas.example.com/joebob_050689_1.mp3" />
</media:group>
<author>jbb@dallas.example.com (Joe Bob Briggs)</author>
<comments>http://example.org</comments>
<title>test</title>
<category domain="foo">bar</category>
<description>I'm headed for France. I wasn't gonna go this year, but then last week &lt;a href="http://www.imdb.com/title/tt0086525/"&gt;Valley Girl&lt;/a&gt; came out and I said to myself, Joe Bob, you gotta get out of the country for a while.</description></item>
</channel>
</rss>