зеркало из https://github.com/mozilla/gecko-dev.git
Bug 852828 - Add basic support for subscribing to feeds (RSS/Atom) r=margaret
This commit is contained in:
Родитель
61331ffc6c
Коммит
c22720d7c0
|
@ -683,6 +683,20 @@ pref("browser.chrome.dynamictoolbar", true);
|
|||
pref("webgl.disabled", true);
|
||||
#endif
|
||||
|
||||
// initial web feed readers list
|
||||
pref("browser.contentHandlers.types.0.title", "chrome://browser/locale/region.properties");
|
||||
pref("browser.contentHandlers.types.0.uri", "chrome://browser/locale/region.properties");
|
||||
pref("browser.contentHandlers.types.0.type", "application/vnd.mozilla.maybe.feed");
|
||||
pref("browser.contentHandlers.types.1.title", "chrome://browser/locale/region.properties");
|
||||
pref("browser.contentHandlers.types.1.uri", "chrome://browser/locale/region.properties");
|
||||
pref("browser.contentHandlers.types.1.type", "application/vnd.mozilla.maybe.feed");
|
||||
pref("browser.contentHandlers.types.2.title", "chrome://browser/locale/region.properties");
|
||||
pref("browser.contentHandlers.types.2.uri", "chrome://browser/locale/region.properties");
|
||||
pref("browser.contentHandlers.types.2.type", "application/vnd.mozilla.maybe.feed");
|
||||
pref("browser.contentHandlers.types.3.title", "chrome://browser/locale/region.properties");
|
||||
pref("browser.contentHandlers.types.3.uri", "chrome://browser/locale/region.properties");
|
||||
pref("browser.contentHandlers.types.3.type", "application/vnd.mozilla.maybe.feed");
|
||||
|
||||
#ifndef RELEASE_BUILD
|
||||
// Enable Web Audio for Firefox for Android in Nightly and Aurora
|
||||
pref("media.webaudio.enabled", true);
|
||||
|
|
|
@ -199,7 +199,7 @@ abstract public class BrowserApp extends GeckoApp
|
|||
case PAGE_SHOW:
|
||||
loadFavicon(tab);
|
||||
break;
|
||||
case LINK_ADDED:
|
||||
case LINK_FAVICON:
|
||||
// If tab is not loading and the favicon is updated, we
|
||||
// want to load the image straight away. If tab is still
|
||||
// loading, we only load the favicon once the page's content
|
||||
|
|
|
@ -204,11 +204,15 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory,
|
|||
menu.findItem(R.id.share).setVisible(false);
|
||||
menu.findItem(R.id.add_to_launcher).setVisible(false);
|
||||
}
|
||||
if (!tab.getFeedsEnabled()) {
|
||||
menu.findItem(R.id.subscribe).setVisible(false);
|
||||
}
|
||||
} else {
|
||||
// if there is no tab, remove anything tab dependent
|
||||
menu.findItem(R.id.copyurl).setVisible(false);
|
||||
menu.findItem(R.id.share).setVisible(false);
|
||||
menu.findItem(R.id.add_to_launcher).setVisible(false);
|
||||
menu.findItem(R.id.subscribe).setVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2549,6 +2549,19 @@ abstract public class GeckoApp
|
|||
shareCurrentUrl();
|
||||
return true;
|
||||
}
|
||||
case R.id.subscribe: {
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null && tab.getFeedsEnabled()) {
|
||||
JSONObject args = new JSONObject();
|
||||
try {
|
||||
args.put("tabId", tab.getId());
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "error building json arguments");
|
||||
}
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Feeds:Subscribe", args.toString()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case R.id.copyurl: {
|
||||
Tab tab = Tabs.getInstance().getSelectedTab();
|
||||
if (tab != null) {
|
||||
|
|
|
@ -38,6 +38,7 @@ public class Tab {
|
|||
private Bitmap mFavicon;
|
||||
private String mFaviconUrl;
|
||||
private int mFaviconSize;
|
||||
private boolean mFeedsEnabled;
|
||||
private JSONObject mIdentityData;
|
||||
private boolean mReaderEnabled;
|
||||
private BitmapDrawable mThumbnail;
|
||||
|
@ -76,6 +77,7 @@ public class Tab {
|
|||
mFavicon = null;
|
||||
mFaviconUrl = null;
|
||||
mFaviconSize = 0;
|
||||
mFeedsEnabled = false;
|
||||
mIdentityData = null;
|
||||
mReaderEnabled = false;
|
||||
mEnteringReaderMode = false;
|
||||
|
@ -189,6 +191,10 @@ public class Tab {
|
|||
return mFaviconUrl;
|
||||
}
|
||||
|
||||
public boolean getFeedsEnabled() {
|
||||
return mFeedsEnabled;
|
||||
}
|
||||
|
||||
public String getSecurityMode() {
|
||||
try {
|
||||
return mIdentityData.getString("mode");
|
||||
|
@ -312,6 +318,10 @@ public class Tab {
|
|||
mFaviconSize = 0;
|
||||
}
|
||||
|
||||
public void setFeedsEnabled(boolean feedsEnabled) {
|
||||
mFeedsEnabled = feedsEnabled;
|
||||
}
|
||||
|
||||
public void updateIdentityData(JSONObject identityData) {
|
||||
mIdentityData = identityData;
|
||||
}
|
||||
|
@ -525,6 +535,7 @@ public class Tab {
|
|||
|
||||
setContentType(message.getString("contentType"));
|
||||
clearFavicon();
|
||||
setFeedsEnabled(false);
|
||||
updateTitle(null);
|
||||
updateIdentityData(null);
|
||||
setReaderEnabled(false);
|
||||
|
|
|
@ -84,7 +84,8 @@ public class Tabs implements GeckoEventListener {
|
|||
registerEventListener("Content:PageShow");
|
||||
registerEventListener("DOMContentLoaded");
|
||||
registerEventListener("DOMTitleChanged");
|
||||
registerEventListener("DOMLinkAdded");
|
||||
registerEventListener("Link:Favicon");
|
||||
registerEventListener("Link:Feed");
|
||||
registerEventListener("DesktopMode:Changed");
|
||||
}
|
||||
|
||||
|
@ -437,9 +438,12 @@ public class Tabs implements GeckoEventListener {
|
|||
notifyListeners(tab, Tabs.TabEvents.LOADED);
|
||||
} else if (event.equals("DOMTitleChanged")) {
|
||||
tab.updateTitle(message.getString("title"));
|
||||
} else if (event.equals("DOMLinkAdded")) {
|
||||
} else if (event.equals("Link:Favicon")) {
|
||||
tab.updateFaviconURL(message.getString("href"), message.getInt("size"));
|
||||
notifyListeners(tab, TabEvents.LINK_ADDED);
|
||||
notifyListeners(tab, TabEvents.LINK_FAVICON);
|
||||
} else if (event.equals("Link:Feed")) {
|
||||
tab.setFeedsEnabled(true);
|
||||
notifyListeners(tab, TabEvents.LINK_FEED);
|
||||
} else if (event.equals("DesktopMode:Changed")) {
|
||||
tab.setDesktopMode(message.getBoolean("desktopMode"));
|
||||
notifyListeners(tab, TabEvents.DESKTOP_MODE_CHANGE);
|
||||
|
@ -492,7 +496,8 @@ public class Tabs implements GeckoEventListener {
|
|||
LOCATION_CHANGE,
|
||||
MENU_UPDATED,
|
||||
PAGE_SHOW,
|
||||
LINK_ADDED,
|
||||
LINK_FAVICON,
|
||||
LINK_FEED,
|
||||
SECURITY_CHANGE,
|
||||
READER_ENABLED,
|
||||
DESKTOP_MODE_CHANGE
|
||||
|
|
|
@ -164,6 +164,7 @@ size. -->
|
|||
<!ENTITY contextmenu_paste "Paste">
|
||||
<!ENTITY contextmenu_copyurl "Copy Address">
|
||||
<!ENTITY contextmenu_edit_bookmark "Edit">
|
||||
<!ENTITY contextmenu_subscribe "Subscribe to Page">
|
||||
|
||||
<!ENTITY history_removed "Page removed">
|
||||
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
<item android:id="@+id/share"
|
||||
android:title="@string/contextmenu_share"/>
|
||||
|
||||
<item android:id="@+id/subscribe"
|
||||
android:title="@string/contextmenu_subscribe"/>
|
||||
|
||||
<item android:id="@+id/copyurl"
|
||||
android:title="@string/contextmenu_copyurl"/>
|
||||
|
||||
|
|
|
@ -171,6 +171,7 @@
|
|||
<string name="contextmenu_paste">&contextmenu_paste;</string>
|
||||
<string name="contextmenu_copyurl">&contextmenu_copyurl;</string>
|
||||
<string name="contextmenu_edit_bookmark">&contextmenu_edit_bookmark;</string>
|
||||
<string name="contextmenu_subscribe">&contextmenu_subscribe;</string>
|
||||
|
||||
<string name="history_removed">&history_removed;</string>
|
||||
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/* 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/. */
|
||||
"use strict";
|
||||
|
||||
var FeedHandler = {
|
||||
PREF_CONTENTHANDLERS_BRANCH: "browser.contentHandlers.types.",
|
||||
TYPE_MAYBE_FEED: "application/vnd.mozilla.maybe.feed",
|
||||
|
||||
_contentTypes: null,
|
||||
|
||||
getContentHandlers: function fh_getContentHandlers(contentType) {
|
||||
if (!this._contentTypes)
|
||||
this.loadContentHandlers();
|
||||
|
||||
if (!(contentType in this._contentTypes))
|
||||
return [];
|
||||
|
||||
return this._contentTypes[contentType];
|
||||
},
|
||||
|
||||
loadContentHandlers: function fh_loadContentHandlers() {
|
||||
this._contentTypes = {};
|
||||
|
||||
let kids = Services.prefs.getBranch(this.PREF_CONTENTHANDLERS_BRANCH).getChildList("");
|
||||
|
||||
// First get the numbers of the providers by getting all ###.uri prefs
|
||||
let nums = [];
|
||||
for (let i = 0; i < kids.length; i++) {
|
||||
let match = /^(\d+)\.uri$/.exec(kids[i]);
|
||||
if (!match)
|
||||
continue;
|
||||
else
|
||||
nums.push(match[1]);
|
||||
}
|
||||
|
||||
// Sort them, to get them back in order
|
||||
nums.sort(function(a, b) { return a - b; });
|
||||
|
||||
// Now register them
|
||||
for (let i = 0; i < nums.length; i++) {
|
||||
let branch = Services.prefs.getBranch(this.PREF_CONTENTHANDLERS_BRANCH + nums[i] + ".");
|
||||
let vals = branch.getChildList("");
|
||||
if (vals.length == 0)
|
||||
return;
|
||||
|
||||
try {
|
||||
let type = branch.getCharPref("type");
|
||||
let uri = branch.getComplexValue("uri", Ci.nsIPrefLocalizedString).data;
|
||||
let title = branch.getComplexValue("title", Ci.nsIPrefLocalizedString).data;
|
||||
|
||||
if (!(type in this._contentTypes))
|
||||
this._contentTypes[type] = [];
|
||||
this._contentTypes[type].push({ contentType: type, uri: uri, name: title });
|
||||
}
|
||||
catch(ex) {}
|
||||
}
|
||||
},
|
||||
|
||||
observe: function fh_observe(aSubject, aTopic, aData) {
|
||||
if (aTopic === "Feeds:Subscribe") {
|
||||
let args = JSON.parse(aData);
|
||||
let tab = BrowserApp.getTabForId(args.tabId);
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
let browser = tab.browser;
|
||||
let feeds = browser.feeds;
|
||||
if (feeds == null)
|
||||
return;
|
||||
|
||||
// First, let's decide on which feed to subscribe
|
||||
let feedIndex = -1;
|
||||
if (feeds.length > 1) {
|
||||
// JSON for Prompt
|
||||
let feedResult = {
|
||||
type: "Prompt:Show",
|
||||
multiple: false,
|
||||
selected: [],
|
||||
listitems: []
|
||||
};
|
||||
|
||||
// Build the list of feeds
|
||||
for (let i = 0; i < feeds.length; i++) {
|
||||
let item = {
|
||||
label: feeds[i].title || feeds[i].href,
|
||||
isGroup: false,
|
||||
inGroup: false,
|
||||
disabled: false,
|
||||
id: i
|
||||
};
|
||||
feedResult.listitems.push(item);
|
||||
}
|
||||
feedIndex = JSON.parse(sendMessageToJava(feedResult)).button;
|
||||
} else {
|
||||
// Only a single feed on the page
|
||||
feedIndex = 0;
|
||||
}
|
||||
|
||||
if (feedIndex == -1)
|
||||
return;
|
||||
let feedURL = feeds[feedIndex].href;
|
||||
|
||||
// Next, we decide on which service to send the feed
|
||||
let handlers = this.getContentHandlers(this.TYPE_MAYBE_FEED);
|
||||
if (handlers.length == 0)
|
||||
return;
|
||||
|
||||
// JSON for Prompt
|
||||
let handlerResult = {
|
||||
type: "Prompt:Show",
|
||||
multiple: false,
|
||||
selected: [],
|
||||
listitems: []
|
||||
};
|
||||
|
||||
// Build the list of handlers
|
||||
for (let i = 0; i < handlers.length; ++i) {
|
||||
let item = {
|
||||
label: handlers[i].name,
|
||||
isGroup: false,
|
||||
inGroup: false,
|
||||
disabled: false,
|
||||
id: i
|
||||
};
|
||||
handlerResult.listitems.push(item);
|
||||
}
|
||||
let handlerIndex = JSON.parse(sendMessageToJava(handlerResult)).button;
|
||||
if (handlerIndex == -1)
|
||||
return;
|
||||
|
||||
// Merge the handler URL and the feed URL
|
||||
let readerURL = handlers[handlerIndex].uri;
|
||||
readerURL = readerURL.replace(/%s/gi, encodeURIComponent(feedURL));
|
||||
|
||||
// Open the resultant URL in a new tab
|
||||
BrowserApp.addTab(readerURL, { parentId: BrowserApp.selectedTab.id });
|
||||
}
|
||||
}
|
||||
};
|
|
@ -95,6 +95,7 @@ var LazyNotificationGetter = {
|
|||
["ConsoleAPI", ["console-api-log-event"], "chrome://browser/content/ConsoleAPI.js"],
|
||||
["FindHelper", ["FindInPage:Find", "FindInPage:Prev", "FindInPage:Next", "FindInPage:Closed", "Tab:Selected"], "chrome://browser/content/FindHelper.js"],
|
||||
["PermissionsHelper", ["Permissions:Get", "Permissions:Clear"], "chrome://browser/content/PermissionsHelper.js"],
|
||||
["FeedHandler", ["Feeds:Subscribe"], "chrome://browser/content/FeedHandler.js"],
|
||||
].forEach(function (aScript) {
|
||||
let [name, notifications, script] = aScript;
|
||||
XPCOMUtils.defineLazyGetter(window, name, function() {
|
||||
|
@ -3017,11 +3018,11 @@ Tab.prototype = {
|
|||
if (!target.href || target.disabled)
|
||||
return;
|
||||
|
||||
// ignore on frames and other documents
|
||||
// Ignore on frames and other documents
|
||||
if (target.ownerDocument != this.browser.contentDocument)
|
||||
return;
|
||||
|
||||
// sanitize the rel string
|
||||
// Sanitize the rel string
|
||||
let list = [];
|
||||
if (target.rel) {
|
||||
list = target.rel.toLowerCase().split(/\s+/);
|
||||
|
@ -3032,42 +3033,60 @@ Tab.prototype = {
|
|||
list.push("[" + rel + "]");
|
||||
}
|
||||
|
||||
// We only care about icon links
|
||||
if (list.indexOf("[icon]") == -1)
|
||||
return;
|
||||
if (list.indexOf("[icon]") != -1) {
|
||||
// We want to get the largest icon size possible for our UI.
|
||||
let maxSize = 0;
|
||||
|
||||
// We want to get the largest icon size possible for our UI.
|
||||
let maxSize = 0;
|
||||
// We use the sizes attribute if available
|
||||
// see http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#rel-icon
|
||||
if (target.hasAttribute("sizes")) {
|
||||
let sizes = target.getAttribute("sizes").toLowerCase();
|
||||
|
||||
// We use the sizes attribute if available
|
||||
// see http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#rel-icon
|
||||
if (target.hasAttribute("sizes")) {
|
||||
let sizes = target.getAttribute("sizes").toLowerCase();
|
||||
|
||||
if (sizes == "any") {
|
||||
// Since Java expects an integer, use -1 to represent icons with sizes="any"
|
||||
maxSize = -1;
|
||||
} else {
|
||||
let tokens = sizes.split(" ");
|
||||
tokens.forEach(function(token) {
|
||||
// TODO: check for invalid tokens
|
||||
let [w, h] = token.split("x");
|
||||
maxSize = Math.max(maxSize, Math.max(w, h));
|
||||
});
|
||||
if (sizes == "any") {
|
||||
// Since Java expects an integer, use -1 to represent icons with sizes="any"
|
||||
maxSize = -1;
|
||||
} else {
|
||||
let tokens = sizes.split(" ");
|
||||
tokens.forEach(function(token) {
|
||||
// TODO: check for invalid tokens
|
||||
let [w, h] = token.split("x");
|
||||
maxSize = Math.max(maxSize, Math.max(w, h));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let json = {
|
||||
type: "Link:Favicon",
|
||||
tabID: this.id,
|
||||
href: resolveGeckoURI(target.href),
|
||||
charset: target.ownerDocument.characterSet,
|
||||
title: target.title,
|
||||
rel: list.join(" "),
|
||||
size: maxSize
|
||||
};
|
||||
sendMessageToJava(json);
|
||||
} else if (list.indexOf("[alternate]") != -1) {
|
||||
let type = target.type.toLowerCase().replace(/^\s+|\s*(?:;.*)?$/g, "");
|
||||
let isFeed = (type == "application/rss+xml" || type == "application/atom+xml");
|
||||
|
||||
if (!isFeed)
|
||||
return;
|
||||
|
||||
try {
|
||||
// urlSecurityCeck will throw if things are not OK
|
||||
ContentAreaUtils.urlSecurityCheck(target.href, target.ownerDocument.nodePrincipal, Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
|
||||
|
||||
if (!this.browser.feeds)
|
||||
this.browser.feeds = [];
|
||||
this.browser.feeds.push({ href: target.href, title: target.title, type: type });
|
||||
|
||||
let json = {
|
||||
type: "Link:Feed",
|
||||
tabID: this.id
|
||||
};
|
||||
sendMessageToJava(json);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
let json = {
|
||||
type: "DOMLinkAdded",
|
||||
tabID: this.id,
|
||||
href: resolveGeckoURI(target.href),
|
||||
charset: target.ownerDocument.characterSet,
|
||||
title: target.title,
|
||||
rel: list.join(" "),
|
||||
size: maxSize
|
||||
};
|
||||
|
||||
sendMessageToJava(json);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ chrome.jar:
|
|||
content/MasterPassword.js (content/MasterPassword.js)
|
||||
content/FindHelper.js (content/FindHelper.js)
|
||||
content/PermissionsHelper.js (content/PermissionsHelper.js)
|
||||
content/FeedHandler.js (content/FeedHandler.js)
|
||||
|
||||
% content branding %content/branding/
|
||||
|
||||
|
|
|
@ -24,3 +24,10 @@ gecko.handlerService.schemes.mailto.0.name=Yahoo! Mail
|
|||
gecko.handlerService.schemes.mailto.0.uriTemplate=http://compose.mail.yahoo.com/?To=%s
|
||||
gecko.handlerService.schemes.mailto.1.name=Gmail
|
||||
gecko.handlerService.schemes.mailto.1.uriTemplate=https://mail.google.com/mail/?extsrc=mailto&url=%s
|
||||
|
||||
# This is the default set of web based feed handlers shown in the reader
|
||||
# selection UI
|
||||
browser.contentHandlers.types.0.title=My Yahoo!
|
||||
browser.contentHandlers.types.0.uri=http://add.my.yahoo.com/rss?url=%s
|
||||
browser.contentHandlers.types.1.title=Google
|
||||
browser.contentHandlers.types.1.uri=http://fusion.google.com/add?feedurl=%s
|
||||
|
|
Загрузка…
Ссылка в новой задаче