Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / browser / components / feeds / src / FeedWriter.js
blobdfc423b64a2e7cbc98272901883272d7afdcd71a
1 # -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 # ***** BEGIN LICENSE BLOCK *****
3 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 # The contents of this file are subject to the Mozilla Public License Version
6 # 1.1 (the "License"); you may not use this file except in compliance with
7 # the License. You may obtain a copy of the License at
8 # http://www.mozilla.org/MPL/
10 # Software distributed under the License is distributed on an "AS IS" basis,
11 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 # for the specific language governing rights and limitations under the
13 # License.
15 # The Original Code is the Feed Writer.
17 # The Initial Developer of the Original Code is Google Inc.
18 # Portions created by the Initial Developer are Copyright (C) 2006
19 # the Initial Developer. All Rights Reserved.
21 # Contributor(s):
22 # Ben Goodger <beng@google.com>
23 # Jeff Walden <jwalden+code@mit.edu>
24 # Asaf Romano <mano@mozilla.com>
25 # Robert Sayre <sayrer@gmail.com>
26 # Michael Ventnor <m.ventnor@gmail.com>
27 # Will Guaraldi <will.guaraldi@pculture.org>
29 # Alternatively, the contents of this file may be used under the terms of
30 # either the GNU General Public License Version 2 or later (the "GPL"), or
31 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 # in which case the provisions of the GPL or the LGPL are applicable instead
33 # of those above. If you wish to allow use of your version of this file only
34 # under the terms of either the GPL or the LGPL, and not to allow others to
35 # use your version of this file under the terms of the MPL, indicate your
36 # decision by deleting the provisions above and replace them with the notice
37 # and other provisions required by the GPL or the LGPL. If you do not delete
38 # the provisions above, a recipient may use your version of this file under
39 # the terms of any one of the MPL, the GPL or the LGPL.
41 # ***** END LICENSE BLOCK ***** */
43 const Cc = Components.classes;
44 const Ci = Components.interfaces;
45 const Cr = Components.results;
46 const Cu = Components.utils;
48 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
50 function LOG(str) {
51 var prefB = Cc["@mozilla.org/preferences-service;1"].
52 getService(Ci.nsIPrefBranch);
54 var shouldLog = false;
55 try {
56 shouldLog = prefB.getBoolPref("feeds.log");
58 catch (ex) {
61 if (shouldLog)
62 dump("*** Feeds: " + str + "\n");
65 /**
66 * Wrapper function for nsIIOService::newURI.
67 * @param aURLSpec
68 * The URL string from which to create an nsIURI.
69 * @returns an nsIURI object, or null if the creation of the URI failed.
71 function makeURI(aURLSpec, aCharset) {
72 var ios = Cc["@mozilla.org/network/io-service;1"].
73 getService(Ci.nsIIOService);
74 try {
75 return ios.newURI(aURLSpec, aCharset, null);
76 } catch (ex) { }
78 return null;
81 const XML_NS = "http://www.w3.org/XML/1998/namespace"
82 const HTML_NS = "http://www.w3.org/1999/xhtml";
83 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
84 const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
85 const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed";
86 const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed";
87 const URI_BUNDLE = "chrome://browser/locale/feeds/subscribe.properties";
88 const SUBSCRIBE_PAGE_URI = "chrome://browser/content/feeds/subscribe.xhtml";
90 const PREF_SELECTED_APP = "browser.feeds.handlers.application";
91 const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
92 const PREF_SELECTED_ACTION = "browser.feeds.handler";
93 const PREF_SELECTED_READER = "browser.feeds.handler.default";
95 const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application";
96 const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice";
97 const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler";
98 const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default";
100 const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application";
101 const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice";
102 const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler";
103 const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default";
105 const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI";
107 const TITLE_ID = "feedTitleText";
108 const SUBTITLE_ID = "feedSubtitleText";
110 function getPrefAppForType(t) {
111 switch (t) {
112 case Ci.nsIFeed.TYPE_VIDEO:
113 return PREF_VIDEO_SELECTED_APP;
115 case Ci.nsIFeed.TYPE_AUDIO:
116 return PREF_AUDIO_SELECTED_APP;
118 default:
119 return PREF_SELECTED_APP;
123 function getPrefWebForType(t) {
124 switch (t) {
125 case Ci.nsIFeed.TYPE_VIDEO:
126 return PREF_VIDEO_SELECTED_WEB;
128 case Ci.nsIFeed.TYPE_AUDIO:
129 return PREF_AUDIO_SELECTED_WEB;
131 default:
132 return PREF_SELECTED_WEB;
136 function getPrefActionForType(t) {
137 switch (t) {
138 case Ci.nsIFeed.TYPE_VIDEO:
139 return PREF_VIDEO_SELECTED_ACTION;
141 case Ci.nsIFeed.TYPE_AUDIO:
142 return PREF_AUDIO_SELECTED_ACTION;
144 default:
145 return PREF_SELECTED_ACTION;
149 function getPrefReaderForType(t) {
150 switch (t) {
151 case Ci.nsIFeed.TYPE_VIDEO:
152 return PREF_VIDEO_SELECTED_READER;
154 case Ci.nsIFeed.TYPE_AUDIO:
155 return PREF_AUDIO_SELECTED_READER;
157 default:
158 return PREF_SELECTED_READER;
163 * Converts a number of bytes to the appropriate unit that results in a
164 * number that needs fewer than 4 digits
166 * @return a pair: [new value with 3 sig. figs., its unit]
168 function convertByteUnits(aBytes) {
169 var units = ["bytes", "kilobyte", "megabyte", "gigabyte"];
170 let unitIndex = 0;
172 // convert to next unit if it needs 4 digits (after rounding), but only if
173 // we know the name of the next unit
174 while ((aBytes >= 999.5) && (unitIndex < units.length - 1)) {
175 aBytes /= 1024;
176 unitIndex++;
179 // Get rid of insignificant bits by truncating to 1 or 0 decimal points
180 // 0 -> 0; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235
181 aBytes = aBytes.toFixed((aBytes > 0) && (aBytes < 100) ? 1 : 0);
183 return [aBytes, units[unitIndex]];
186 function FeedWriter() {}
187 FeedWriter.prototype = {
188 _mimeSvc : Cc["@mozilla.org/mime;1"].
189 getService(Ci.nsIMIMEService),
191 _getPropertyAsBag: function FW__getPropertyAsBag(container, property) {
192 return container.fields.getProperty(property).
193 QueryInterface(Ci.nsIPropertyBag2);
196 _getPropertyAsString: function FW__getPropertyAsString(container, property) {
197 try {
198 return container.fields.getPropertyAsAString(property);
200 catch (e) {
202 return "";
205 _setContentText: function FW__setContentText(id, text) {
206 this._contentSandbox.element = this._document.getElementById(id);
207 this._contentSandbox.textNode = this._document.createTextNode(text);
208 var codeStr =
209 "while (element.hasChildNodes()) " +
210 " element.removeChild(element.firstChild);" +
211 "element.appendChild(textNode);";
212 Cu.evalInSandbox(codeStr, this._contentSandbox);
213 this._contentSandbox.element = null;
214 this._contentSandbox.textNode = null;
218 * Safely sets the href attribute on an anchor tag, providing the URI
219 * specified can be loaded according to rules.
220 * @param element
221 * The element to set a URI attribute on
222 * @param attribute
223 * The attribute of the element to set the URI to, e.g. href or src
224 * @param uri
225 * The URI spec to set as the href
227 _safeSetURIAttribute:
228 function FW__safeSetURIAttribute(element, attribute, uri) {
229 var secman = Cc["@mozilla.org/scriptsecuritymanager;1"].
230 getService(Ci.nsIScriptSecurityManager);
231 const flags = Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL;
232 try {
233 secman.checkLoadURIStrWithPrincipal(this._feedPrincipal, uri, flags);
234 // checkLoadURIStrWithPrincipal will throw if the link URI should not be
235 // loaded, either because our feedURI isn't allowed to load it or per
236 // the rules specified in |flags|, so we'll never "linkify" the link...
238 catch (e) {
239 // Not allowed to load this link because secman.checkLoadURIStr threw
240 return;
243 this._contentSandbox.element = element;
244 this._contentSandbox.uri = uri;
245 var codeStr = "element.setAttribute('" + attribute + "', uri);";
246 Cu.evalInSandbox(codeStr, this._contentSandbox);
250 * Use this sandbox to run any dom manipulation code on nodes which
251 * are already inserted into the content document.
253 __contentSandbox: null,
254 get _contentSandbox() {
255 if (!this.__contentSandbox)
256 this.__contentSandbox = new Cu.Sandbox(this._window);
258 return this.__contentSandbox;
262 * Calls doCommand for a the given XUL element within the context of the
263 * content document.
265 * @param aElement
266 * the XUL element to call doCommand() on.
268 _safeDoCommand: function FW___safeDoCommand(aElement) {
269 this._contentSandbox.element = aElement;
270 Cu.evalInSandbox("element.doCommand();", this._contentSandbox);
271 this._contentSandbox.element = null;
274 __faviconService: null,
275 get _faviconService() {
276 if (!this.__faviconService)
277 this.__faviconService = Cc["@mozilla.org/browser/favicon-service;1"].
278 getService(Ci.nsIFaviconService);
280 return this.__faviconService;
283 __bundle: null,
284 get _bundle() {
285 if (!this.__bundle) {
286 this.__bundle = Cc["@mozilla.org/intl/stringbundle;1"].
287 getService(Ci.nsIStringBundleService).
288 createBundle(URI_BUNDLE);
290 return this.__bundle;
293 _getFormattedString: function FW__getFormattedString(key, params) {
294 return this._bundle.formatStringFromName(key, params, params.length);
297 _getString: function FW__getString(key) {
298 return this._bundle.GetStringFromName(key);
301 /* Magic helper methods to be used instead of xbl properties */
302 _getSelectedItemFromMenulist: function FW__getSelectedItemFromList(aList) {
303 var node = aList.firstChild.firstChild;
304 while (node) {
305 if (node.localName == "menuitem" && node.getAttribute("selected") == "true")
306 return node;
308 node = node.nextSibling;
311 return null;
314 _setCheckboxCheckedState: function FW__setCheckboxCheckedState(aCheckbox, aValue) {
315 // see checkbox.xml, xbl bindings are not applied within the sandbox!
316 this._contentSandbox.checkbox = aCheckbox;
317 var codeStr;
318 var change = (aValue != (aCheckbox.getAttribute('checked') == 'true'));
319 if (aValue)
320 codeStr = "checkbox.setAttribute('checked', 'true'); ";
321 else
322 codeStr = "checkbox.removeAttribute('checked'); ";
324 if (change) {
325 this._contentSandbox.document = this._document;
326 codeStr += "var event = document.createEvent('Events'); " +
327 "event.initEvent('CheckboxStateChange', true, true);" +
328 "checkbox.dispatchEvent(event);"
331 Cu.evalInSandbox(codeStr, this._contentSandbox);
335 * Returns a date suitable for displaying in the feed preview.
336 * If the date cannot be parsed, the return value is "false".
337 * @param dateString
338 * A date as extracted from a feed entry. (entry.updated)
340 _parseDate: function FW__parseDate(dateString) {
341 // Convert the date into the user's local time zone
342 dateObj = new Date(dateString);
344 // Make sure the date we're given is valid.
345 if (!dateObj.getTime())
346 return false;
348 var dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"].
349 getService(Ci.nsIScriptableDateFormat);
350 return dateService.FormatDateTime("", dateService.dateFormatLong, dateService.timeFormatNoSeconds,
351 dateObj.getFullYear(), dateObj.getMonth()+1, dateObj.getDate(),
352 dateObj.getHours(), dateObj.getMinutes(), dateObj.getSeconds());
356 * Returns the feed type.
358 __feedType: null,
359 _getFeedType: function FW__getFeedType() {
360 if (this.__feedType != null)
361 return this.__feedType;
363 try {
364 // grab the feed because it's got the feed.type in it.
365 var container = this._getContainer();
366 var feed = container.QueryInterface(Ci.nsIFeed);
367 this.__feedType = feed.type;
368 return feed.type;
369 } catch (ex) { }
371 return Ci.nsIFeed.TYPE_FEED;
375 * Maps a feed type to a maybe-feed mimetype.
377 _getMimeTypeForFeedType: function FW__getMimeTypeForFeedType() {
378 switch (this._getFeedType()) {
379 case Ci.nsIFeed.TYPE_VIDEO:
380 return TYPE_MAYBE_VIDEO_FEED;
382 case Ci.nsIFeed.TYPE_AUDIO:
383 return TYPE_MAYBE_AUDIO_FEED;
385 default:
386 return TYPE_MAYBE_FEED;
391 * Writes the feed title into the preview document.
392 * @param container
393 * The feed container
395 _setTitleText: function FW__setTitleText(container) {
396 if (container.title) {
397 var title = container.title.plainText();
398 this._setContentText(TITLE_ID, title);
399 this._contentSandbox.document = this._document;
400 this._contentSandbox.title = title;
401 var codeStr = "document.title = title;"
402 Cu.evalInSandbox(codeStr, this._contentSandbox);
405 var feed = container.QueryInterface(Ci.nsIFeed);
406 if (feed && feed.subtitle)
407 this._setContentText(SUBTITLE_ID, container.subtitle.plainText());
411 * Writes the title image into the preview document if one is present.
412 * @param container
413 * The feed container
415 _setTitleImage: function FW__setTitleImage(container) {
416 try {
417 var parts = container.image;
419 // Set up the title image (supplied by the feed)
420 var feedTitleImage = this._document.getElementById("feedTitleImage");
421 this._safeSetURIAttribute(feedTitleImage, "src",
422 parts.getPropertyAsAString("url"));
424 // Set up the title image link
425 var feedTitleLink = this._document.getElementById("feedTitleLink");
427 var titleText = this._getFormattedString("linkTitleTextFormat",
428 [parts.getPropertyAsAString("title")]);
429 this._contentSandbox.feedTitleLink = feedTitleLink;
430 this._contentSandbox.titleText = titleText;
431 this._contentSandbox.feedTitleText = this._document.getElementById("feedTitleText");
432 this._contentSandbox.titleImageWidth = parseInt(parts.getPropertyAsAString("width")) + 15;
434 // Fix the margin on the main title, so that the image doesn't run over
435 // the underline
436 var codeStr = "feedTitleLink.setAttribute('title', titleText); " +
437 "feedTitleText.style.marginRight = titleImageWidth + 'px';";
438 Cu.evalInSandbox(codeStr, this._contentSandbox);
439 this._contentSandbox.feedTitleLink = null;
440 this._contentSandbox.titleText = null;
441 this._contentSandbox.feedTitleText = null;
442 this._contentSandbox.titleImageWidth = null;
444 this._safeSetURIAttribute(feedTitleLink, "href",
445 parts.getPropertyAsAString("link"));
447 catch (e) {
448 LOG("Failed to set Title Image (this is benign): " + e);
453 * Writes all entries contained in the feed.
454 * @param container
455 * The container of entries in the feed
457 _writeFeedContent: function FW__writeFeedContent(container) {
458 // Build the actual feed content
459 var feed = container.QueryInterface(Ci.nsIFeed);
460 if (feed.items.length == 0)
461 return;
463 this._contentSandbox.feedContent =
464 this._document.getElementById("feedContent");
466 for (var i = 0; i < feed.items.length; ++i) {
467 var entry = feed.items.queryElementAt(i, Ci.nsIFeedEntry);
468 entry.QueryInterface(Ci.nsIFeedContainer);
470 var entryContainer = this._document.createElementNS(HTML_NS, "div");
471 entryContainer.className = "entry";
473 // If the entry has a title, make it a link
474 if (entry.title) {
475 var a = this._document.createElementNS(HTML_NS, "a");
476 a.appendChild(this._document.createTextNode(entry.title.plainText()));
478 // Entries are not required to have links, so entry.link can be null.
479 if (entry.link)
480 this._safeSetURIAttribute(a, "href", entry.link.spec);
482 var title = this._document.createElementNS(HTML_NS, "h3");
483 title.appendChild(a);
485 var lastUpdated = this._parseDate(entry.updated);
486 if (lastUpdated) {
487 var dateDiv = this._document.createElementNS(HTML_NS, "div");
488 dateDiv.className = "lastUpdated";
489 dateDiv.textContent = lastUpdated;
490 title.appendChild(dateDiv);
493 entryContainer.appendChild(title);
496 var body = this._document.createElementNS(HTML_NS, "div");
497 var summary = entry.summary || entry.content;
498 var docFragment = null;
499 if (summary) {
500 if (summary.base)
501 body.setAttributeNS(XML_NS, "base", summary.base.spec);
502 else
503 LOG("no base?");
504 docFragment = summary.createDocumentFragment(body);
505 if (docFragment)
506 body.appendChild(docFragment);
508 // If the entry doesn't have a title, append a # permalink
509 // See http://scripting.com/rss.xml for an example
510 if (!entry.title && entry.link) {
511 var a = this._document.createElementNS(HTML_NS, "a");
512 a.appendChild(this._document.createTextNode("#"));
513 this._safeSetURIAttribute(a, "href", entry.link.spec);
514 body.appendChild(this._document.createTextNode(" "));
515 body.appendChild(a);
519 body.className = "feedEntryContent";
520 entryContainer.appendChild(body);
522 if (entry.enclosures && entry.enclosures.length > 0) {
523 var enclosuresDiv = this._buildEnclosureDiv(entry);
524 entryContainer.appendChild(enclosuresDiv);
527 this._contentSandbox.entryContainer = entryContainer;
528 this._contentSandbox.clearDiv =
529 this._document.createElementNS(HTML_NS, "div");
530 this._contentSandbox.clearDiv.style.clear = "both";
532 var codeStr = "feedContent.appendChild(entryContainer); " +
533 "feedContent.appendChild(clearDiv);"
534 Cu.evalInSandbox(codeStr, this._contentSandbox);
537 this._contentSandbox.feedContent = null;
538 this._contentSandbox.entryContainer = null;
539 this._contentSandbox.clearDiv = null;
543 * Takes a url to a media item and returns the best name it can come up with.
544 * Frequently this is the filename portion (e.g. passing in
545 * http://example.com/foo.mpeg would return "foo.mpeg"), but in more complex
546 * cases, this will return the entire url (e.g. passing in
547 * http://example.com/somedirectory/ would return
548 * http://example.com/somedirectory/).
549 * @param aURL
550 * The URL string from which to create a display name
551 * @returns a string
553 _getURLDisplayName: function FW__getURLDisplayName(aURL) {
554 var url = makeURI(aURL);
555 url.QueryInterface(Ci.nsIURL);
556 if (url == null || url.fileName.length == 0)
557 return aURL;
559 return decodeURI(url.fileName);
563 * Takes a FeedEntry with enclosures, generates the HTML code to represent
564 * them, and returns that.
565 * @param entry
566 * FeedEntry with enclosures
567 * @returns element
569 _buildEnclosureDiv: function FW__buildEnclosureDiv(entry) {
570 var enclosuresDiv = this._document.createElementNS(HTML_NS, "div");
571 enclosuresDiv.className = "enclosures";
573 enclosuresDiv.appendChild(this._document.createTextNode(this._getString("mediaLabel")));
575 var roundme = function(n) {
576 return (Math.round(n * 100) / 100).toLocaleString();
579 for (var i_enc = 0; i_enc < entry.enclosures.length; ++i_enc) {
580 var enc = entry.enclosures.queryElementAt(i_enc, Ci.nsIWritablePropertyBag2);
582 if (!(enc.hasKey("url")))
583 continue;
585 var enclosureDiv = this._document.createElementNS(HTML_NS, "div");
586 enclosureDiv.setAttribute("class", "enclosure");
588 var mozicon = "moz-icon://.txt?size=16";
589 var type_text = null;
590 var size_text = null;
592 if (enc.hasKey("type")) {
593 type_text = enc.get("type");
594 try {
595 var handlerInfoWrapper = this._mimeSvc.getFromTypeAndExtension(enc.get("type"), null);
597 if (handlerInfoWrapper)
598 type_text = handlerInfoWrapper.description;
600 if (type_text && type_text.length > 0)
601 mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type");
603 } catch (ex) { }
607 if (enc.hasKey("length") && /^[0-9]+$/.test(enc.get("length"))) {
608 var enc_size = convertByteUnits(parseInt(enc.get("length")));
610 var size_text = this._getFormattedString("enclosureSizeText",
611 [enc_size[0], this._getString(enc_size[1])]);
614 var iconimg = this._document.createElementNS(HTML_NS, "img");
615 iconimg.setAttribute("src", mozicon);
616 iconimg.setAttribute("class", "type-icon");
617 enclosureDiv.appendChild(iconimg);
619 enclosureDiv.appendChild(this._document.createTextNode( " " ));
621 var enc_href = this._document.createElementNS(HTML_NS, "a");
622 enc_href.appendChild(this._document.createTextNode(this._getURLDisplayName(enc.get("url"))));
623 this._safeSetURIAttribute(enc_href, "href", enc.get("url"));
624 enclosureDiv.appendChild(enc_href);
626 if (type_text && size_text)
627 enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ", " + size_text + ")"));
629 else if (type_text)
630 enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ")"))
632 else if (size_text)
633 enclosureDiv.appendChild(this._document.createTextNode( " (" + size_text + ")"))
635 enclosuresDiv.appendChild(enclosureDiv);
638 return enclosuresDiv;
642 * Gets a valid nsIFeedContainer object from the parsed nsIFeedResult.
643 * Displays error information if there was one.
644 * @param result
645 * The parsed feed result
646 * @returns A valid nsIFeedContainer object containing the contents of
647 * the feed.
649 _getContainer: function FW__getContainer(result) {
650 var feedService =
651 Cc["@mozilla.org/browser/feeds/result-service;1"].
652 getService(Ci.nsIFeedResultService);
654 try {
655 var result =
656 feedService.getFeedResult(this._getOriginalURI(this._window));
658 catch (e) {
659 LOG("Subscribe Preview: feed not available?!");
662 if (result.bozo) {
663 LOG("Subscribe Preview: feed result is bozo?!");
666 try {
667 var container = result.doc;
669 catch (e) {
670 LOG("Subscribe Preview: no result.doc? Why didn't the original reload?");
671 return null;
673 return container;
677 * Get the human-readable display name of a file. This could be the
678 * application name.
679 * @param file
680 * A nsIFile to look up the name of
681 * @returns The display name of the application represented by the file.
683 _getFileDisplayName: function FW__getFileDisplayName(file) {
684 #ifdef XP_WIN
685 if (file instanceof Ci.nsILocalFileWin) {
686 try {
687 return file.getVersionInfoField("FileDescription");
689 catch (e) {
692 #endif
693 #ifdef XP_MACOSX
694 var lfm = file.QueryInterface(Ci.nsILocalFileMac);
695 try {
696 return lfm.bundleDisplayName;
698 catch (e) {
699 // fall through to the file name
701 #endif
702 var ios =
703 Cc["@mozilla.org/network/io-service;1"].
704 getService(Ci.nsIIOService);
705 var url = ios.newFileURI(file).QueryInterface(Ci.nsIURL);
706 return url.fileName;
710 * Get moz-icon url for a file
711 * @param file
712 * A nsIFile object for which the moz-icon:// is returned
713 * @returns moz-icon url of the given file as a string
715 _getFileIconURL: function FW__getFileIconURL(file) {
716 var ios = Cc["@mozilla.org/network/io-service;1"].
717 getService(Components.interfaces.nsIIOService);
718 var fph = ios.getProtocolHandler("file")
719 .QueryInterface(Ci.nsIFileProtocolHandler);
720 var urlSpec = fph.getURLSpecFromFile(file);
721 return "moz-icon://" + urlSpec + "?size=16";
725 * Helper method to set the selected application and system default
726 * reader menuitems details from a file object
727 * @param aMenuItem
728 * The menuitem on which the attributes should be set
729 * @param aFile
730 * The menuitem's associated file
732 _initMenuItemWithFile: function(aMenuItem, aFile) {
733 this._contentSandbox.menuitem = aMenuItem;
734 this._contentSandbox.label = this._getFileDisplayName(aFile);
735 this._contentSandbox.image = this._getFileIconURL(aFile);
736 var codeStr = "menuitem.setAttribute('label', label); " +
737 "menuitem.setAttribute('image', image);"
738 Cu.evalInSandbox(codeStr, this._contentSandbox);
742 * Displays a prompt from which the user may choose a (client) feed reader.
743 * @return - true if a feed reader was selected, false otherwise.
745 _chooseClientApp: function FW__chooseClientApp() {
746 try {
747 var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
748 fp.init(this._window,
749 this._getString("chooseApplicationDialogTitle"),
750 Ci.nsIFilePicker.modeOpen);
751 fp.appendFilters(Ci.nsIFilePicker.filterApps);
753 if (fp.show() == Ci.nsIFilePicker.returnOK) {
754 this._selectedApp = fp.file;
755 if (this._selectedApp) {
756 // XXXben - we need to compare this with the running instance executable
757 // just don't know how to do that via script...
758 // XXXmano TBD: can probably add this to nsIShellService
759 #ifdef XP_WIN
760 #expand if (fp.file.leafName != "__MOZ_APP_NAME__.exe") {
761 #else
762 #ifdef XP_MACOSX
763 #expand if (fp.file.leafName != "__MOZ_APP_DISPLAYNAME__.app") {
764 #else
765 #expand if (fp.file.leafName != "__MOZ_APP_NAME__-bin") {
766 #endif
767 #endif
768 this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem,
769 this._selectedApp);
771 // Show and select the selected application menuitem
772 var codeStr = "selectedAppMenuItem.hidden = false;" +
773 "selectedAppMenuItem.doCommand();"
774 Cu.evalInSandbox(codeStr, this._contentSandbox);
775 return true;
780 catch(ex) { }
782 return false;
785 _setAlwaysUseCheckedState: function FW__setAlwaysUseCheckedState(feedType) {
786 var checkbox = this._document.getElementById("alwaysUse");
787 if (checkbox) {
788 var alwaysUse = false;
789 try {
790 var prefs = Cc["@mozilla.org/preferences-service;1"].
791 getService(Ci.nsIPrefBranch);
792 if (prefs.getCharPref(getPrefActionForType(feedType)) != "ask")
793 alwaysUse = true;
795 catch(ex) { }
796 this._setCheckboxCheckedState(checkbox, alwaysUse);
800 _setSubscribeUsingLabel: function FW__setSubscribeUsingLabel() {
801 var stringLabel = "subscribeFeedUsing";
802 switch (this._getFeedType()) {
803 case Ci.nsIFeed.TYPE_VIDEO:
804 stringLabel = "subscribeVideoPodcastUsing";
805 break;
807 case Ci.nsIFeed.TYPE_AUDIO:
808 stringLabel = "subscribeAudioPodcastUsing";
809 break;
812 this._contentSandbox.subscribeUsing =
813 this._document.getElementById("subscribeUsingDescription");
814 this._contentSandbox.label = this._getString(stringLabel);
815 var codeStr = "subscribeUsing.setAttribute('value', label);"
816 Cu.evalInSandbox(codeStr, this._contentSandbox);
819 _setAlwaysUseLabel: function FW__setAlwaysUseLabel() {
820 var checkbox = this._document.getElementById("alwaysUse");
821 if (checkbox) {
822 var handlersMenuList = this._document.getElementById("handlersMenuList");
823 if (handlersMenuList) {
824 var handlerName = this._getSelectedItemFromMenulist(handlersMenuList)
825 .getAttribute("label");
826 var stringLabel = "alwaysUseForFeeds";
827 switch (this._getFeedType()) {
828 case Ci.nsIFeed.TYPE_VIDEO:
829 stringLabel = "alwaysUseForVideoPodcasts";
830 break;
832 case Ci.nsIFeed.TYPE_AUDIO:
833 stringLabel = "alwaysUseForAudioPodcasts";
834 break;
837 this._contentSandbox.checkbox = checkbox;
838 this._contentSandbox.label = this._getFormattedString(stringLabel, [handlerName]);
840 var codeStr = "checkbox.setAttribute('label', label);";
841 Cu.evalInSandbox(codeStr, this._contentSandbox);
846 // nsIDomEventListener
847 handleEvent: function(event) {
848 // see comments in init()
849 event = new XPCNativeWrapper(event);
850 if (event.target.ownerDocument != this._document) {
851 LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!");
852 return;
855 if (event.type == "command") {
856 switch (event.target.id) {
857 case "subscribeButton":
858 this.subscribe();
859 break;
860 case "chooseApplicationMenuItem":
861 /* Bug 351263: Make sure to not steal focus if the "Choose
862 * Application" item is being selected with the keyboard. We do this
863 * by ignoring command events while the dropdown is closed (user
864 * arrowing through the combobox), but handling them while the
865 * combobox dropdown is open (user pressed enter when an item was
866 * selected). If we don't show the filepicker here, it will be shown
867 * when clicking "Subscribe Now".
869 var popupbox = this._document.getElementById("handlersMenuList")
870 .firstChild.boxObject;
871 popupbox.QueryInterface(Components.interfaces.nsIPopupBoxObject);
872 if (popupbox.popupState == "hiding" && !this._chooseClientApp()) {
873 // Select the (per-prefs) selected handler if no application was
874 // selected
875 this._setSelectedHandler(this._getFeedType());
877 break;
878 default:
879 this._setAlwaysUseLabel();
884 _setSelectedHandler: function FW__setSelectedHandler(feedType) {
885 var prefs =
886 Cc["@mozilla.org/preferences-service;1"].
887 getService(Ci.nsIPrefBranch);
889 var handler = "bookmarks";
890 try {
891 handler = prefs.getCharPref(getPrefReaderForType(feedType));
893 catch (ex) { }
895 switch (handler) {
896 case "web": {
897 var handlersMenuList = this._document.getElementById("handlersMenuList");
898 if (handlersMenuList) {
899 var url = prefs.getComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString).data;
900 var handlers =
901 handlersMenuList.getElementsByAttribute("webhandlerurl", url);
902 if (handlers.length == 0) {
903 LOG("FeedWriter._setSelectedHandler: selected web handler isn't in the menulist")
904 return;
907 this._safeDoCommand(handlers[0]);
909 break;
911 case "client": {
912 try {
913 this._selectedApp =
914 prefs.getComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile);
916 catch(ex) {
917 this._selectedApp = null;
920 if (this._selectedApp) {
921 this._initMenuItemWithFile(this._contentSandbox.selectedAppMenuItem,
922 this._selectedApp);
923 var codeStr = "selectedAppMenuItem.hidden = false; " +
924 "selectedAppMenuItem.doCommand(); ";
926 // Only show the default reader menuitem if the default reader
927 // isn't the selected application
928 if (this._defaultSystemReader) {
929 var shouldHide =
930 this._defaultSystemReader.path == this._selectedApp.path;
931 codeStr += "defaultHandlerMenuItem.hidden = " + shouldHide + ";"
933 Cu.evalInSandbox(codeStr, this._contentSandbox);
934 break;
937 case "bookmarks":
938 default: {
939 var liveBookmarksMenuItem = this._document.getElementById("liveBookmarksMenuItem");
940 if (liveBookmarksMenuItem)
941 this._safeDoCommand(liveBookmarksMenuItem);
946 _initSubscriptionUI: function FW__initSubscriptionUI() {
947 var handlersMenuPopup = this._document.getElementById("handlersMenuPopup");
948 if (!handlersMenuPopup)
949 return;
951 var feedType = this._getFeedType();
952 var codeStr;
954 // change the background
955 var header = this._document.getElementById("feedHeader");
956 this._contentSandbox.header = header;
957 switch (feedType) {
958 case Ci.nsIFeed.TYPE_VIDEO:
959 codeStr = "header.className = 'videoPodcastBackground'; ";
960 break;
962 case Ci.nsIFeed.TYPE_AUDIO:
963 codeStr = "header.className = 'audioPodcastBackground'; ";
964 break;
966 default:
967 codeStr = "header.className = 'feedBackground'; ";
971 // Last-selected application
972 var menuItem = this._document.createElementNS(XUL_NS, "menuitem");
973 menuItem.id = "selectedAppMenuItem";
974 menuItem.className = "menuitem-iconic";
975 menuItem.setAttribute("handlerType", "client");
976 try {
977 var prefs = Cc["@mozilla.org/preferences-service;1"].
978 getService(Ci.nsIPrefBranch);
979 this._selectedApp = prefs.getComplexValue(getPrefAppForType(feedType),
980 Ci.nsILocalFile);
982 if (this._selectedApp.exists())
983 this._initMenuItemWithFile(menuItem, this._selectedApp);
984 else {
985 // Hide the menuitem if the last selected application doesn't exist
986 menuItem.setAttribute("hidden", true);
989 catch(ex) {
990 // Hide the menuitem until an application is selected
991 menuItem.setAttribute("hidden", true);
993 this._contentSandbox.handlersMenuPopup = handlersMenuPopup;
994 this._contentSandbox.selectedAppMenuItem = menuItem;
996 codeStr += "handlersMenuPopup.appendChild(selectedAppMenuItem); ";
998 // List the default feed reader
999 try {
1000 this._defaultSystemReader = Cc["@mozilla.org/browser/shell-service;1"].
1001 getService(Ci.nsIShellService).
1002 defaultFeedReader;
1003 menuItem = this._document.createElementNS(XUL_NS, "menuitem");
1004 menuItem.id = "defaultHandlerMenuItem";
1005 menuItem.className = "menuitem-iconic";
1006 menuItem.setAttribute("handlerType", "client");
1008 this._initMenuItemWithFile(menuItem, this._defaultSystemReader);
1010 // Hide the default reader item if it points to the same application
1011 // as the last-selected application
1012 if (this._selectedApp &&
1013 this._selectedApp.path == this._defaultSystemReader.path)
1014 menuItem.hidden = true;
1016 catch(ex) { menuItem = null; /* no default reader */ }
1018 if (menuItem) {
1019 this._contentSandbox.defaultHandlerMenuItem = menuItem;
1020 codeStr += "handlersMenuPopup.appendChild(defaultHandlerMenuItem); ";
1023 // "Choose Application..." menuitem
1024 menuItem = this._document.createElementNS(XUL_NS, "menuitem");
1025 menuItem.id = "chooseApplicationMenuItem";
1026 menuItem.setAttribute("label", this._getString("chooseApplicationMenuItem"));
1028 this._contentSandbox.chooseAppMenuItem = menuItem;
1029 codeStr += "handlersMenuPopup.appendChild(chooseAppMenuItem); ";
1031 // separator
1032 this._contentSandbox.chooseAppSep =
1033 this._document.createElementNS(XUL_NS, "menuseparator")
1034 codeStr += "handlersMenuPopup.appendChild(chooseAppSep); ";
1036 Cu.evalInSandbox(codeStr, this._contentSandbox);
1038 var historySvc = Cc["@mozilla.org/browser/nav-history-service;1"].
1039 getService(Ci.nsINavHistoryService);
1040 historySvc.addObserver(this, false);
1042 // List of web handlers
1043 var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
1044 getService(Ci.nsIWebContentConverterService);
1045 var handlers = wccr.getContentHandlers(this._getMimeTypeForFeedType(feedType), {});
1046 if (handlers.length != 0) {
1047 for (var i = 0; i < handlers.length; ++i) {
1048 menuItem = this._document.createElementNS(XUL_NS, "menuitem");
1049 menuItem.className = "menuitem-iconic";
1050 menuItem.setAttribute("label", handlers[i].name);
1051 menuItem.setAttribute("handlerType", "web");
1052 menuItem.setAttribute("webhandlerurl", handlers[i].uri);
1053 this._contentSandbox.menuItem = menuItem;
1054 codeStr = "handlersMenuPopup.appendChild(menuItem);";
1055 Cu.evalInSandbox(codeStr, this._contentSandbox);
1057 // For privacy reasons we cannot set the image attribute directly
1058 // to the icon url, see Bug 358878
1059 var uri = makeURI(handlers[i].uri);
1060 if (!this._setFaviconForWebReader(uri, menuItem)) {
1061 if (uri && /^https?/.test(uri.scheme)) {
1062 var iconURL = makeURI(uri.prePath + "/favicon.ico");
1063 this._faviconService.setAndLoadFaviconForPage(uri, iconURL, true);
1067 this._contentSandbox.menuItem = null;
1070 this._setSelectedHandler(feedType);
1072 // "Subscribe using..."
1073 this._setSubscribeUsingLabel();
1075 // "Always use..." checkbox initial state
1076 this._setAlwaysUseCheckedState(feedType);
1077 this._setAlwaysUseLabel();
1079 // We update the "Always use.." checkbox label whenever the selected item
1080 // in the list is changed
1081 handlersMenuPopup.addEventListener("command", this, false);
1083 // Set up the "Subscribe Now" button
1084 this._document
1085 .getElementById("subscribeButton")
1086 .addEventListener("command", this, false);
1088 // first-run ui
1089 var showFirstRunUI = true;
1090 try {
1091 showFirstRunUI = prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI);
1093 catch (ex) { }
1094 if (showFirstRunUI) {
1095 var textfeedinfo1, textfeedinfo2;
1096 switch (feedType) {
1097 case Ci.nsIFeed.TYPE_VIDEO:
1098 textfeedinfo1 = "feedSubscriptionVideoPodcast1";
1099 textfeedinfo2 = "feedSubscriptionVideoPodcast2";
1100 break;
1101 case Ci.nsIFeed.TYPE_AUDIO:
1102 textfeedinfo1 = "feedSubscriptionAudioPodcast1";
1103 textfeedinfo2 = "feedSubscriptionAudioPodcast2";
1104 break;
1105 default:
1106 textfeedinfo1 = "feedSubscriptionFeed1";
1107 textfeedinfo2 = "feedSubscriptionFeed2";
1110 this._contentSandbox.feedinfo1 =
1111 this._document.getElementById("feedSubscriptionInfo1");
1112 this._contentSandbox.feedinfo1Str = this._getString(textfeedinfo1);
1113 this._contentSandbox.feedinfo2 =
1114 this._document.getElementById("feedSubscriptionInfo2");
1115 this._contentSandbox.feedinfo2Str = this._getString(textfeedinfo2);
1116 this._contentSandbox.header = header;
1117 codeStr = "feedinfo1.value = feedinfo1Str; " +
1118 "feedinfo2.value = feedinfo2Str; " +
1119 "header.setAttribute('firstrun', 'true');"
1120 Cu.evalInSandbox(codeStr, this._contentSandbox);
1121 prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false);
1126 * Returns the original URI object of the feed and ensures that this
1127 * component is only ever invoked from the preview document.
1128 * @param aWindow
1129 * The window of the document invoking the BrowserFeedWriter
1131 _getOriginalURI: function FW__getOriginalURI(aWindow) {
1132 var chan = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).
1133 getInterface(Ci.nsIWebNavigation).
1134 QueryInterface(Ci.nsIDocShell).currentDocumentChannel;
1136 var uri = makeURI(SUBSCRIBE_PAGE_URI);
1137 var resolvedURI = Cc["@mozilla.org/chrome/chrome-registry;1"].
1138 getService(Ci.nsIChromeRegistry).
1139 convertChromeURL(uri);
1141 if (resolvedURI.equals(chan.URI))
1142 return chan.originalURI;
1144 return null;
1147 _window: null,
1148 _document: null,
1149 _feedURI: null,
1150 _feedPrincipal: null,
1152 // nsIFeedWriter
1153 init: function FW_init(aWindow) {
1154 // Explicitly wrap |window| in an XPCNativeWrapper to make sure
1155 // it's a real native object! This will throw an exception if we
1156 // get a non-native object.
1157 var window = new XPCNativeWrapper(aWindow);
1158 this._feedURI = this._getOriginalURI(window);
1159 if (!this._feedURI)
1160 return;
1162 this._window = window;
1163 this._document = window.document;
1165 var secman = Cc["@mozilla.org/scriptsecuritymanager;1"].
1166 getService(Ci.nsIScriptSecurityManager);
1167 this._feedPrincipal = secman.getCodebasePrincipal(this._feedURI);
1169 LOG("Subscribe Preview: feed uri = " + this._window.location.href);
1171 // Set up the subscription UI
1172 this._initSubscriptionUI();
1173 var prefs = Cc["@mozilla.org/preferences-service;1"].
1174 getService(Ci.nsIPrefBranch2);
1175 prefs.addObserver(PREF_SELECTED_ACTION, this, false);
1176 prefs.addObserver(PREF_SELECTED_READER, this, false);
1177 prefs.addObserver(PREF_SELECTED_WEB, this, false);
1178 prefs.addObserver(PREF_SELECTED_APP, this, false);
1179 prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, false);
1180 prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, false);
1181 prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, false);
1182 prefs.addObserver(PREF_VIDEO_SELECTED_APP, this, false);
1184 prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this, false);
1185 prefs.addObserver(PREF_AUDIO_SELECTED_READER, this, false);
1186 prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this, false);
1187 prefs.addObserver(PREF_AUDIO_SELECTED_APP, this, false);
1190 writeContent: function FW_writeContent() {
1191 if (!this._window)
1192 return;
1194 try {
1195 // Set up the feed content
1196 var container = this._getContainer();
1197 if (!container)
1198 return;
1200 this._setTitleText(container);
1201 this._setTitleImage(container);
1202 this._writeFeedContent(container);
1204 finally {
1205 this._removeFeedFromCache();
1209 close: function FW_close() {
1210 this._document
1211 .getElementById("handlersMenuPopup")
1212 .removeEventListener("command", this, false);
1213 this._document
1214 .getElementById("subscribeButton")
1215 .removeEventListener("command", this, false);
1216 this._document = null;
1217 this._window = null;
1218 var prefs = Cc["@mozilla.org/preferences-service;1"].
1219 getService(Ci.nsIPrefBranch2);
1220 prefs.removeObserver(PREF_SELECTED_ACTION, this);
1221 prefs.removeObserver(PREF_SELECTED_READER, this);
1222 prefs.removeObserver(PREF_SELECTED_WEB, this);
1223 prefs.removeObserver(PREF_SELECTED_APP, this);
1224 prefs.removeObserver(PREF_VIDEO_SELECTED_ACTION, this);
1225 prefs.removeObserver(PREF_VIDEO_SELECTED_READER, this);
1226 prefs.removeObserver(PREF_VIDEO_SELECTED_WEB, this);
1227 prefs.removeObserver(PREF_VIDEO_SELECTED_APP, this);
1229 prefs.removeObserver(PREF_AUDIO_SELECTED_ACTION, this);
1230 prefs.removeObserver(PREF_AUDIO_SELECTED_READER, this);
1231 prefs.removeObserver(PREF_AUDIO_SELECTED_WEB, this);
1232 prefs.removeObserver(PREF_AUDIO_SELECTED_APP, this);
1234 this._removeFeedFromCache();
1235 this.__faviconService = null;
1236 this.__bundle = null;
1237 this._feedURI = null;
1238 this.__contentSandbox = null;
1240 var historySvc = Cc["@mozilla.org/browser/nav-history-service;1"].
1241 getService(Ci.nsINavHistoryService);
1242 historySvc.removeObserver(this);
1245 _removeFeedFromCache: function FW__removeFeedFromCache() {
1246 if (this._feedURI) {
1247 var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
1248 getService(Ci.nsIFeedResultService);
1249 feedService.removeFeedResult(this._feedURI);
1250 this._feedURI = null;
1254 subscribe: function FW_subscribe() {
1255 var feedType = this._getFeedType();
1257 // Subscribe to the feed using the selected handler and save prefs
1258 var prefs = Cc["@mozilla.org/preferences-service;1"].
1259 getService(Ci.nsIPrefBranch);
1260 var defaultHandler = "reader";
1261 var useAsDefault = this._document.getElementById("alwaysUse")
1262 .getAttribute("checked");
1264 var handlersMenuList = this._document.getElementById("handlersMenuList");
1265 var selectedItem = this._getSelectedItemFromMenulist(handlersMenuList);
1267 // Show the file picker before subscribing if the
1268 // choose application menuitem was choosen using the keyboard
1269 if (selectedItem.id == "chooseApplicationMenuItem") {
1270 if (!this._chooseClientApp())
1271 return;
1273 selectedItem = this._getSelectedItemFromMenulist(handlersMenuList);
1276 if (selectedItem.hasAttribute("webhandlerurl")) {
1277 var webURI = selectedItem.getAttribute("webhandlerurl");
1278 prefs.setCharPref(getPrefReaderForType(feedType), "web");
1280 var supportsString = Cc["@mozilla.org/supports-string;1"].
1281 createInstance(Ci.nsISupportsString);
1282 supportsString.data = webURI;
1283 prefs.setComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString,
1284 supportsString);
1286 var wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
1287 getService(Ci.nsIWebContentConverterService);
1288 var handler = wccr.getWebContentHandlerByURI(this._getMimeTypeForFeedType(feedType), webURI);
1289 if (handler) {
1290 if (useAsDefault)
1291 wccr.setAutoHandler(this._getMimeTypeForFeedType(feedType), handler);
1293 this._window.location.href = handler.getHandlerURI(this._window.location.href);
1296 else {
1297 switch (selectedItem.id) {
1298 case "selectedAppMenuItem":
1299 prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile,
1300 this._selectedApp);
1301 prefs.setCharPref(getPrefReaderForType(feedType), "client");
1302 break;
1303 case "defaultHandlerMenuItem":
1304 prefs.setComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile,
1305 this._defaultSystemReader);
1306 prefs.setCharPref(getPrefReaderForType(feedType), "client");
1307 break;
1308 case "liveBookmarksMenuItem":
1309 defaultHandler = "bookmarks";
1310 prefs.setCharPref(getPrefReaderForType(feedType), "bookmarks");
1311 break;
1313 var feedService = Cc["@mozilla.org/browser/feeds/result-service;1"].
1314 getService(Ci.nsIFeedResultService);
1316 // Pull the title and subtitle out of the document
1317 var feedTitle = this._document.getElementById(TITLE_ID).textContent;
1318 var feedSubtitle = this._document.getElementById(SUBTITLE_ID).textContent;
1319 feedService.addToClientReader(this._window.location.href, feedTitle, feedSubtitle, feedType);
1322 // If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
1323 // to either "reader" (If a web reader or if an application is selected),
1324 // or to "bookmarks" (if the live bookmarks option is selected).
1325 // Otherwise, we should set it to "ask"
1326 if (useAsDefault)
1327 prefs.setCharPref(getPrefActionForType(feedType), defaultHandler);
1328 else
1329 prefs.setCharPref(getPrefActionForType(feedType), "ask");
1332 // nsIObserver
1333 observe: function FW_observe(subject, topic, data) {
1334 // see init()
1335 subject = new XPCNativeWrapper(subject);
1337 if (!this._window) {
1338 // this._window is null unless this.init was called with a trusted
1339 // window object.
1340 return;
1343 var feedType = this._getFeedType();
1345 if (topic == "nsPref:changed") {
1346 switch (data) {
1347 case PREF_SELECTED_READER:
1348 case PREF_SELECTED_WEB:
1349 case PREF_SELECTED_APP:
1350 case PREF_VIDEO_SELECTED_READER:
1351 case PREF_VIDEO_SELECTED_WEB:
1352 case PREF_VIDEO_SELECTED_APP:
1353 case PREF_AUDIO_SELECTED_READER:
1354 case PREF_AUDIO_SELECTED_WEB:
1355 case PREF_AUDIO_SELECTED_APP:
1356 this._setSelectedHandler(feedType);
1357 break;
1358 case PREF_SELECTED_ACTION:
1359 case PREF_VIDEO_SELECTED_ACTION:
1360 case PREF_AUDIO_SELECTED_ACTION:
1361 this._setAlwaysUseCheckedState(feedType);
1367 * Sets the icon for the given web-reader item in the readers menu
1368 * if the favicon-service has the necessary icon stored.
1369 * @param aURI
1370 * the reader URI.
1371 * @param aMenuItem
1372 * the reader item in the readers menulist.
1373 * @return true if the icon was set, false otherwise.
1375 _setFaviconForWebReader:
1376 function FW__setFaviconForWebReader(aURI, aMenuItem) {
1377 var faviconsSvc = this._faviconService;
1378 var faviconURI = null;
1379 try {
1380 faviconURI = faviconsSvc.getFaviconForPage(aURI);
1382 catch(ex) { }
1384 if (faviconURI) {
1385 var dataURL = faviconsSvc.getFaviconDataAsDataURL(faviconURI);
1386 if (dataURL) {
1387 this._contentSandbox.menuItem = aMenuItem;
1388 this._contentSandbox.dataURL = dataURL;
1389 var codeStr = "menuItem.setAttribute('image', dataURL);";
1390 Cu.evalInSandbox(codeStr, this._contentSandbox);
1391 this._contentSandbox.menuItem = null;
1392 this._contentSandbox.dataURL = null;
1394 return true;
1398 return false;
1401 // nsINavHistoryService
1402 onPageChanged: function FW_onPageChanged(aURI, aWhat, aValue) {
1403 // see init()
1404 aURI = new XPCNativeWrapper(aURI);
1406 if (aWhat == Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
1407 // Go through the readers menu and look for the corresponding
1408 // reader menu-item for the page if any.
1409 var spec = aURI.spec;
1410 var handlersMenulist = this._document.getElementById("handlersMenuList");
1411 var possibleHandlers = handlersMenulist.firstChild.childNodes;
1412 for (var i=0; i < possibleHandlers.length ; i++) {
1413 if (possibleHandlers[i].getAttribute("webhandlerurl") == spec) {
1414 this._setFaviconForWebReader(aURI, possibleHandlers[i]);
1415 return;
1421 onBeginUpdateBatch: function() { },
1422 onEndUpdateBatch: function() { },
1423 onVisit: function() { },
1424 onTitleChanged: function() { },
1425 onDeleteURI: function() { },
1426 onClearHistory: function() { },
1427 onPageExpired: function() { },
1429 // nsIClassInfo
1430 getInterfaces: function FW_getInterfaces(countRef) {
1431 var interfaces = [Ci.nsIFeedWriter, Ci.nsIClassInfo, Ci.nsISupports];
1432 countRef.value = interfaces.length;
1433 return interfaces;
1435 getHelperForLanguage: function FW_getHelperForLanguage(language) null,
1436 contractID: "@mozilla.org/browser/feeds/result-writer;1",
1437 classDescription: "Feed Writer",
1438 classID: Components.ID("{49bb6593-3aff-4eb3-a068-2712c28bd58e}"),
1439 implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
1440 flags: Ci.nsIClassInfo.DOM_OBJECT,
1441 _xpcom_categories: [{ category: "JavaScript global constructor",
1442 entry: "BrowserFeedWriter"}],
1443 QueryInterface: XPCOMUtils.generateQI([Ci.nsIFeedWriter, Ci.nsIClassInfo,
1444 Ci.nsIDOMEventListener, Ci.nsIObserver,
1445 Ci.nsINavHistoryObserver])
1448 function NSGetModule(cm, file)
1449 XPCOMUtils.generateModule([FeedWriter]);