diff --git a/mozilla/browser/base/content/browser.js b/mozilla/browser/base/content/browser.js
index 63de19b9492..12e40fd6ff4 100644
--- a/mozilla/browser/base/content/browser.js
+++ b/mozilla/browser/base/content/browser.js
@@ -935,11 +935,12 @@ function prepareForStartup()
// hook up UI through progress listener
gBrowser.addProgressListener(window.XULBrowserWindow, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
- // Initialize the feedhandler
- FeedHandler.init();
+ // setup our common DOMLinkAdded listener
+ gBrowser.addEventListener("DOMLinkAdded",
+ function (event) { DOMLinkHandler.onLinkAdded(event); },
+ false);
- // Initialize the searchbar
- BrowserSearch.init();
+ gBrowser.addEventListener("pagehide", FeedHandler.onPageHide, false);
}
function delayedStartup()
@@ -2647,84 +2648,145 @@ var DownloadsButtonDNDObserver = {
}
}
-const BrowserSearch = {
-
- /**
- * Initialize the BrowserSearch
- */
- init: function() {
- gBrowser.addEventListener("DOMLinkAdded",
- function (event) { BrowserSearch.onLinkAdded(event); },
- false);
- },
-
- /**
- * A new tag has been discovered - check to see if it advertises
- * a OpenSearch engine.
- */
+const DOMLinkHandler = {
onLinkAdded: function(event) {
- // XXX this event listener can/should probably be combined with the onLinkAdded
- // listener in tabbrowser.xml. See comments in FeedHandler.onLinkAdded().
- const target = event.target;
- var etype = target.type;
- const searchRelRegex = /(^|\s)search($|\s)/i;
- const searchHrefRegex = /^(https?|ftp):\/\//i;
-
- if (!etype)
- return;
-
- // Bug 349431: If the engine has no suggested title, ignore it rather
- // than trying to find an alternative.
- if (!target.title)
+ var link = event.originalTarget;
+ var rel = link.rel && link.rel.toLowerCase();
+ if (!link || !link.ownerDocument || !rel || !link.href)
return;
- if (etype == "application/opensearchdescription+xml" &&
- searchRelRegex.test(target.rel) && searchHrefRegex.test(target.href))
- {
- const targetDoc = target.ownerDocument;
- // Set the attribute of the (first) search-engine button.
- var searchButton = document.getAnonymousElementByAttribute(this.getSearchBar(),
- "anonid", "searchbar-engine-button");
- if (searchButton) {
- var browser = gBrowser.getBrowserForDocument(targetDoc);
- // Append the URI and an appropriate title to the browser data.
- var iconURL = null;
- if (gBrowser.shouldLoadFavIcon(browser.currentURI))
- iconURL = browser.currentURI.prePath + "/favicon.ico";
+ var feedAdded = false;
+ var iconAdded = false;
+ var searchAdded = false;
+ var relStrings = rel.split(/\s+/);
+ var rels = {};
+ for (let i = 0; i < relStrings.length; i++)
+ rels[relStrings[i]] = true;
- var hidden = false;
- // If this engine (identified by title) is already in the list, add it
- // to the list of hidden engines rather than to the main list.
- // XXX This will need to be changed when engines are identified by URL;
- // see bug 335102.
- var searchService =
- Components.classes["@mozilla.org/browser/search-service;1"]
- .getService(Components.interfaces.nsIBrowserSearchService);
- if (searchService.getEngineByName(target.title))
- hidden = true;
+ for (let relVal in rels) {
+ switch (relVal) {
+ case "feed":
+ case "alternate":
+ if (!feedAdded) {
+ if (!rels.feed && rels.alternate && rels.stylesheet)
+ break;
- var engines = [];
- if (hidden) {
- if (browser.hiddenEngines)
- engines = browser.hiddenEngines;
- }
- else {
- if (browser.engines)
- engines = browser.engines;
- }
+ var feed = { title: link.title, href: link.href, type: link.type };
+ if (isValidFeed(feed, link.ownerDocument.nodePrincipal, rels.feed)) {
+ FeedHandler.addFeed(feed, link.ownerDocument);
+ feedAdded = true;
+ }
+ }
+ break;
+ case "icon":
+ if (!iconAdded) {
+ if (!gBrowser.mPrefs.getBoolPref("browser.chrome.site_icons"))
+ break;
- engines.push({ uri: target.href,
- title: target.title,
- icon: iconURL });
+ try {
+ var contentPolicy = Cc["@mozilla.org/layout/content-policy;1"].
+ getService(Ci.nsIContentPolicy);
+ } catch(e) {
+ break; // Refuse to load if we can't do a security check.
+ }
- if (hidden) {
- browser.hiddenEngines = engines;
- }
- else {
- browser.engines = engines;
- if (browser == gBrowser || browser == gBrowser.mCurrentBrowser)
- this.updateSearchButton();
- }
+ var targetDoc = link.ownerDocument;
+ var ios = Cc["@mozilla.org/network/io-service;1"].
+ getService(Ci.nsIIOService);
+ var uri = ios.newURI(link.href, targetDoc.characterSet, null);
+ try {
+ // Verify that the load of this icon is legal.
+ // error pages can load their favicon, to be on the safe side,
+ // only allow chrome:// favicons
+ const aboutNeterr = "about:neterror?";
+ if (targetDoc.documentURI.substr(0, aboutNeterr.length) != aboutNeterr ||
+ !uri.schemeIs("chrome")) {
+ var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].
+ getService(Ci.nsIScriptSecurityManager);
+ ssm.checkLoadURIWithPrincipal(targetDoc.nodePrincipal, uri,
+ Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+ }
+ } catch(e) {
+ break;
+ }
+
+ // Security says okay, now ask content policy
+ if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_IMAGE,
+ uri, targetDoc.documentURIObject,
+ link, link.type, null)
+ != Ci.nsIContentPolicy.ACCEPT)
+ break;
+
+ var browserIndex = gBrowser.getBrowserIndexForDocument(targetDoc);
+ // no browser? no favicon.
+ if (browserIndex == -1)
+ break;
+
+ var tab = gBrowser.mTabContainer.childNodes[browserIndex];
+ gBrowser.setIcon(tab, link.href);
+ iconAdded = true;
+ }
+ break;
+ case "search":
+ if (!searchAdded) {
+ var type = link.type && link.type.toLowerCase();
+ type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
+
+ if (type == "application/opensearchdescription+xml" && link.title &&
+ /^(https?|ftp):/i.test(link.href)) {
+ var engine = { title: link.title, href: link.href };
+ BrowserSearch.addEngine(engine, link.ownerDocument);
+ searchAdded = true;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+const BrowserSearch = {
+ addEngine: function(engine, targetDoc) {
+ // Set the attribute of the (first) search-engine button.
+ var searchButton = document.getAnonymousElementByAttribute(this.getSearchBar(), "anonid",
+ "searchbar-engine-button");
+ if (searchButton) {
+ var browser = gBrowser.getBrowserForDocument(targetDoc);
+ // Append the URI and an appropriate title to the browser data.
+ var iconURL = null;
+ if (gBrowser.shouldLoadFavIcon(browser.currentURI))
+ iconURL = browser.currentURI.prePath + "/favicon.ico";
+
+ var hidden = false;
+ // If this engine (identified by title) is already in the list, add it
+ // to the list of hidden engines rather than to the main list.
+ // XXX This will need to be changed when engines are identified by URL;
+ // see bug 335102.
+ var searchService = Cc["@mozilla.org/browser/search-service;1"].
+ getService(Ci.nsIBrowserSearchService);
+ if (searchService.getEngineByName(engine.title))
+ hidden = true;
+
+ var engines = [];
+ if (hidden) {
+ if (browser.hiddenEngines)
+ engines = browser.hiddenEngines;
+ }
+ else {
+ if (browser.engines)
+ engines = browser.engines;
+ }
+
+ engines.push({ uri: engine.href,
+ title: engine.title,
+ icon: iconURL });
+
+ if (hidden)
+ browser.hiddenEngines = engines;
+ else {
+ browser.engines = engines;
+ if (browser == gBrowser || browser == gBrowser.mCurrentBrowser)
+ this.updateSearchButton();
}
}
},
@@ -5073,16 +5135,6 @@ function convertFromUnicode(charset, str)
* and shows UI when they are discovered.
*/
var FeedHandler = {
- /**
- * Initialize the Feed Handler
- */
- init: function() {
- gBrowser.addEventListener("DOMLinkAdded",
- function (event) { FeedHandler.onLinkAdded(event); },
- true);
- gBrowser.addEventListener("pagehide", FeedHandler.onPageHide, true);
- },
-
onPageHide: function(event) {
var theBrowser = gBrowser.getBrowserForDocument(event.target);
if (theBrowser)
@@ -5237,25 +5289,9 @@ var FeedHandler = {
}
}
},
-
- /**
- * A new tag has been discovered - check to see if it advertises
- * an RSS feed.
- */
- onLinkAdded: function(event) {
- // XXX this event listener can/should probably be combined with the onLinkAdded
- // listener in tabbrowser.xml, which only listens for favicons and then passes
- // them to onLinkIconAvailable in the ProgressListener. We could extend the
- // progress listener to have a generic onLinkAvailable and have tabbrowser pass
- // along all events. It should give us the browser for the tab, as well as
- // the actual event.
-
- var feed = recognizeFeedFromLink(event.target,
- event.target.ownerDocument.nodePrincipal);
+ addFeed: function(feed, targetDoc) {
if (feed) {
- const targetDoc = event.target.ownerDocument;
-
// find which tab this is for, and set the attribute on the browser
var browserForLink = gBrowser.getBrowserForDocument(targetDoc);
if (!browserForLink) {
diff --git a/mozilla/browser/base/content/pageinfo/feeds.js b/mozilla/browser/base/content/pageinfo/feeds.js
index e0d5dd28eab..f5f00f3146c 100644
--- a/mozilla/browser/base/content/pageinfo/feeds.js
+++ b/mozilla/browser/base/content/pageinfo/feeds.js
@@ -50,14 +50,23 @@ function initFeedTab()
var linkNodes = gDocument.getElementsByTagName("link");
var length = linkNodes.length;
for (var i = 0; i < length; i++) {
- var feed = recognizeFeedFromLink(linkNodes[i], gDocument.nodePrincipal);
- if (feed) {
- var type = feed.type;
- if (type in feedTypes)
- type = feedTypes[type];
- else
- type = feedTypes["application/rss+xml"];
- addRow(feed.title, type, feed.href);
+ var link = linkNodes[i];
+ if (!link.href)
+ continue;
+
+ var rel = link.rel && link.rel.toLowerCase();
+ var rels = {};
+ if (rel) {
+ for each (let relVal in rel.split(/\s+/))
+ rels[relVal] = true;
+ }
+
+ if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
+ var feed = { title: link.title, href: link.href, type: link.type || "" };
+ if (isValidFeed(feed, gDocument.nodePrincipal, rels.feed)) {
+ var type = feedTypes[feed.type] || feedTypes["application/rss+xml"];
+ addRow(feed.title, type, feed.href);
+ }
}
}
diff --git a/mozilla/browser/base/content/tabbrowser.xml b/mozilla/browser/base/content/tabbrowser.xml
index 4502b77cb75..333fdc88f49 100644
--- a/mozilla/browser/base/content/tabbrowser.xml
+++ b/mozilla/browser/base/content/tabbrowser.xml
@@ -853,73 +853,6 @@