Bug 349380 - better UI for registerContentHandler. r=gavin.

This commit is contained in:
mozilla.mano%sent.com 2006-11-09 18:45:18 +00:00
parent 816cbcbc26
commit 8e1afe52e9
12 changed files with 187 additions and 349 deletions

View File

@ -1,150 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Add Feed Reader Dialog.
*
* The Initial Developer of the Original Code is Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <beng@google.com>
*
* 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
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
function LOG(str) {
dump("*** " + str + "\n");
}
const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed";
const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
const TYPETYPE_MIME = 1;
const TYPETYPE_PROTOCOL = 2;
//
// window.arguments:
//
// 0 nsIDialogParamBlock containing user decision result
// 1 string uri of the service being registered
// 2 string title of the service being registered
// 3 string type of service being registered for
// 4 integer 1 = content type 2 = protocol
var AddFeedReader = {
_result: null,
_uri: null,
_title: null,
_type: null,
_typeType: null,
init: function AFR_init() {
this._result = window.arguments[0].QueryInterface(Ci.nsIDialogParamBlock);
this._uri = window.arguments[1];
this._title = window.arguments[2];
this._type = window.arguments[3];
this._typeType = window.arguments[4];
var strings = document.getElementById("strings");
var dlg = document.documentElement;
var addQuestion = document.getElementById("addQuestion");
var wccr =
Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].
getService(Ci.nsIWebContentConverterService);
var handler =
wccr.getWebContentHandlerByURI(this._type, this._uri);
var key = handler != null ? "handlerRegistered" : "addHandler";
var message = strings.getFormattedString(key, [this._title]);
addQuestion.setAttribute("value", message);
this._updateAddAsDefaultCheckbox();
if (this._type != TYPE_MAYBE_FEED && this._typeType == TYPETYPE_MIME) {
var mimeService =
Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
getService(Ci.nsIMIMEService);
var ext = mimeService.getPrimaryExtension(this._type, null);
var imageBox = document.getElementById("imageBox");
imageBox.style.backgroundImage = "url('moz-icon://goat." + ext + "?size=32');";
}
var site = document.getElementById("site");
site.value = this._uri;
if (handler)
dlg.getButton("accept").focus();
else {
dlg.getButton("accept").label = strings.getString("addHandlerYes");
dlg.getButton("cancel").label = strings.getString("addHandlerNo");
dlg.getButton("cancel").focus();
}
},
_updateAddAsDefaultCheckbox: function AFR__updateAddAsDefaultCheckbox() {
var addAsDefaultCheckbox =
document.getElementById("addAsDefaultCheckbox");
if (this._type != TYPE_MAYBE_FEED) {
addAsDefaultCheckbox.hidden = true;
return;
}
try {
var ps =
Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
var webHandler =
ps.getComplexValue(PREF_SELECTED_WEB, Ci.nsIPrefLocalizedString);
if (webHandler.data == window.arguments[0]) {
addAsDefaultCheckbox.checked = true;
addAsDefaultCheckbox.disabled = true;
}
}
catch (e) {
}
},
add: function AFR_add() {
// Used to tell the WCCR that the user chose to add the handler (rather
// than canceling) and whether or not they made it their default handler.
const PARAM_SHOULD_ADD_HANDLER = 0;
const PARAM_SHOULD_MAKE_DEFAULT = 1;
this._result.SetInt(PARAM_SHOULD_ADD_HANDLER, 1);
if (this._type == TYPE_MAYBE_FEED) {
var addAsDefaultCheckbox = document.getElementById("addAsDefaultCheckbox");
this._result.SetInt(PARAM_SHOULD_MAKE_DEFAULT,
addAsDefaultCheckbox.checked ? 1 : 0);
}
}
};

View File

@ -1,42 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE dialog SYSTEM "chrome://browser/locale/feeds/addFeedReader.dtd">
<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/feeds/addFeedReader.css"?>
<dialog id="addFeedReader"
title="&addFeedReader.title;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="AddFeedReader.init();"
ondialogaccept="AddFeedReader.add();"
style="width: 30em;"
buttons="accept,cancel">
<script type="application/x-javascript"
src="chrome://browser/content/feeds/addFeedReader.js"/>
<stringbundle id="strings"
src="chrome://browser/locale/feeds/subscribe.properties"/>
<hbox flex="1">
<vbox id="imageBox"/>
<vbox flex="1" id="content">
<label id="addQuestion" flex="1"/>
<hbox align="center">
<label id="siteLabel">&forSite.label;</label>
<textbox id="site" readonly="true" flex="1" class="plain"/>
</hbox>
<separator class="thin"/>
<hbox>
<checkbox id="addAsDefaultCheckbox"
label="&addAsDefault.label;"
accesskey="&addAsDefault.accesskey;"/>
</hbox>
</vbox>
</hbox>
</dialog>

View File

@ -1,5 +1,3 @@
browser.jar:
* content/browser/feeds/subscribe.xhtml (content/subscribe.xhtml)
* content/browser/feeds/subscribe.js (content/subscribe.js)
* content/browser/feeds/addFeedReader.xul (content/addFeedReader.xul)
* content/browser/feeds/addFeedReader.js (content/addFeedReader.js)

View File

@ -555,7 +555,7 @@ FeedWriter.prototype = {
case "web": {
var handlersMenuList = this._document.getElementById("handlersMenuList");
if (handlersMenuList) {
var url = prefs.getCharPref(PREF_SELECTED_WEB);
var url = prefs.getComplexValue(PREF_SELECTED_WEB, Ci.nsISupportsString).data;
var handlers =
handlersMenuList.getElementsByAttribute("webhandlerurl", url);
if (handlers.length == 0) {
@ -867,7 +867,12 @@ FeedWriter.prototype = {
if (selectedItem.hasAttribute("webhandlerurl")) {
var webURI = selectedItem.getAttribute("webhandlerurl");
prefs.setCharPref(PREF_SELECTED_READER, "web");
prefs.setCharPref(PREF_SELECTED_WEB, webURI);
var supportsString = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
supportsString.data = webURI;
prefs.setComplexValue(PREF_SELECTED_WEB, Ci.nsISupportsString,
supportsString);
var wccr =
Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"].

View File

@ -20,6 +20,7 @@
#
# Contributor(s):
# Ben Goodger <beng@google.com>
# Asaf Romano <mano@mozilla.com>
#
# 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
@ -57,6 +58,9 @@ const PREF_CONTENTHANDLERS_AUTO = "browser.contentHandlers.auto.";
const PREF_CONTENTHANDLERS_BRANCH = "browser.contentHandlers.types.";
const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice";
const PREF_SELECTED_ACTION = "browser.feeds.handler";
const PREF_SELECTED_READER = "browser.feeds.handler.default";
const STRING_BUNDLE_URI = "chrome://browser/locale/feeds/subscribe.properties";
function WebContentConverter() {
}
@ -148,9 +152,29 @@ ServiceInfo.prototype = {
};
var WebContentConverterRegistrar = {
_stringBundle: null,
get stringBundle() {
if (!this._stringBundle) {
this._stringBundle = Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService).
createBundle(STRING_BUNDLE_URI);
}
return this._stringBundle;
},
_getFormattedString: function WCCR__getFormattedString(key, params) {
return this.stringBundle.formatStringFromName(key, params, params.length);
},
_getString: function WCCR_getString(key) {
return this.stringBundle.GetStringFromName(key);
},
_contentTypes: { },
_protocols: { },
/**
* Track auto handlers for various content types using a content-type to
* handler map.
@ -290,100 +314,20 @@ var WebContentConverterRegistrar = {
return this._mappings[contentType];
return contentType;
},
_wrapString: function WCCR__wrapString(string) {
var supportsString =
Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
supportsString.data = string;
return supportsString;
_makeURI: function(aURL, aOriginCharset, aBaseURI) {
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
return ioService.newURI(aURL, aOriginCharset, aBaseURI);
},
_updateDefaultReader: function WCCR__updateDefaultReader(uri) {
var ps =
Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
var localizedString =
Cc["@mozilla.org/pref-localizedstring;1"].
createInstance(Ci.nsIPrefLocalizedString);
localizedString.data = uri;
ps.setComplexValue(PREF_SELECTED_WEB, Ci.nsIPrefLocalizedString,
localizedString);
var needToUpdateHandler = true;
try {
needToUpdateHandler = ps.getCharPref(PREF_SELECTED_ACTION) != "web";
}
catch (e) {
}
if (needToUpdateHandler)
ps.setCharPref(PREF_SELECTED_ACTION, "web");
},
_confirmAddHandler: function WCCR__confirmAddHandler(contentType, title, uri) {
var args =
Cc["@mozilla.org/supports-array;1"].
createInstance(Ci.nsISupportsArray);
var paramBlock =
Cc["@mozilla.org/embedcomp/dialogparam;1"].
createInstance(Ci.nsIDialogParamBlock);
// Used to tell the WCCR that the user chose to add the handler (rather
// than canceling) and whether or not they made it their default handler.
const PARAM_SHOULD_ADD_HANDLER = 0;
const PARAM_SHOULD_MAKE_DEFAULT = 1;
paramBlock.SetInt(PARAM_SHOULD_ADD_HANDLER, 0);
paramBlock.SetInt(PARAM_SHOULD_MAKE_DEFAULT, 0);
args.AppendElement(paramBlock);
args.AppendElement(this._wrapString(uri));
args.AppendElement(this._wrapString(title));
args.AppendElement(this._wrapString(contentType));
var typeType =
Cc["@mozilla.org/supports-PRInt32;1"].
createInstance(Ci.nsISupportsPRInt32);
typeType.data = 1;
args.AppendElement(typeType);
var ww =
Cc["@mozilla.org/embedcomp/window-watcher;1"].
getService(Ci.nsIWindowWatcher);
ww.openWindow(null, "chrome://browser/content/feeds/addFeedReader.xul",
"", "modal,titlebar,centerscreen,dialog=yes", args);
var shouldAdd = paramBlock.GetInt(PARAM_SHOULD_ADD_HANDLER) == 1;
if (shouldAdd&& contentType == TYPE_MAYBE_FEED &&
paramBlock.GetInt(PARAM_SHOULD_MAKE_DEFAULT) == 1) {
// User chose to use the reader as their default, so update the
// chosen reader preference, too.
this._updateDefaultReader(uri);
}
return shouldAdd;
},
_checkForDuplicateContentType:
function WCCR__checkForDuplicateContentType(contentType, uri, title) {
contentType = this._resolveContentType(contentType);
if (this._typeIsRegistered(contentType, uri)) {
// Show a special dialog for the feed case (XXXben - generalize at some
// point to allow other types to register specialized prompts).
this._confirmAddHandler(contentType, title, uri);
return false;
}
return true;
},
/**
* See nsIWebContentHandlerRegistrar
*/
registerProtocolHandler:
function WCCR_registerProtocolHandler(protocol, uri, title, contentWindow) {
// XXXben - for Firefox 2 we only support feed types
function WCCR_registerProtocolHandler(aProtocol, aURI, aTitle, aContentWindow) {
// not yet implemented
return;
LOG("registerProtocolHandler(" + protocol + "," + uri + "," + title + ")");
if (this._confirmAddHandler(protocol, title, uri))
this._protocols[protocol] = uri;
},
/**
@ -393,27 +337,157 @@ var WebContentConverterRegistrar = {
* preferences.
*/
registerContentHandler:
function WCCR_registerContentHandler(contentType, uri, title, contentWindow) {
LOG("registerContentHandler(" + contentType + "," + uri + "," + title + ")");
// XXXben - for Firefox 2 we only support feed types
contentType = this._resolveContentType(contentType);
function WCCR_registerContentHandler(aContentType, aURIString, aTitle, aContentWindow) {
LOG("registerContentHandler(" + aContentType + "," + aURIString + "," + aTitle + ")");
// We only support feed types at present.
var contentType = this._resolveContentType(aContentType);
if (contentType != TYPE_MAYBE_FEED)
return;
if (!this._checkForDuplicateContentType(contentType, uri, title) ||
!this._confirmAddHandler(contentType, title, uri))
try {
var uri = this._makeURI(aURIString);
}
catch(ex) {
// XXX: Bug 350273
return;
}
// Reset the auto handler so that the user is asked again the next time
// they load content of this type.
if (this.getAutoHandler(contentType))
this.setAutoHandler(contentType, null);
// For security reasons we reject non-http(s) urls (see bug Bug 354316),
// we may need to revise this once we support more content types
if (uri.scheme != "http" && uri.scheme != "https")
throw("Permission denied to add " + uri.spec + "as a content handler");
this._registerContentHandler(contentType, uri, title);
this._saveContentHandlerToPrefs(contentType, uri, title);
var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow);
var browserElement = this._getBrowserForContentWindow(browserWindow, aContentWindow);
var notificationBox = browserWindow.getBrowser().getNotificationBox(browserElement);
this._appendFeedReaderNotification(uri, aTitle, notificationBox);
},
/**
* Returns the browser chrome window in which the content window is in
*/
_getBrowserWindowForContentWindow:
function WCCR__getBrowserWindowForContentWindow(aContentWindow) {
return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow)
.wrappedJSObject;
},
/**
* Returns the <xul:browser> element associated with the given content
* window.
*
* @param aBrowserWindow
* The browser window in which the content window is in.
* @param aContentWindow
* The content window. It's possible to pass a child content window
* (i.e. the content window of a frame/iframe).
*/
_getBrowserForContentWindow:
function WCCR__getBrowserForContentWindow(aBrowserWindow, aContentWindow) {
// This depends on pseudo APIs of browser.js and tabbrowser.xml
aContentWindow = aContentWindow.top;
var browsers = aBrowserWindow.getBrowser().browsers;
for (var i = 0; i < browsers.length; ++i) {
if (browsers[i].contentWindow == aContentWindow)
return browsers[i];
}
},
/**
* Appends a notifcation for the given feed reader details.
*
* The notification could be either a pseudo-dialog which lets
* the user to add the feed reader:
* [ [icon] Add %feed-reader-name% (%feed-reader-host%) as a Feed Reader? (Add) [x] ]
*
* or a simple message for the case where the feed reader is already registered:
* [ [icon] %feed-reader-name% is already registered as a Feed Reader [x] ]
*
* A new notification isn't appended if the given notificationbox has a
* notification for the same feed reader.
*
* @param aURI
* The url of the feed reader as a nsIURI object
* @param aName
* The feed reader name as it was passed to registerContentHandler
* @param aNotificationBox
* The notification box to which a notification might be appended
* @return true if a notification has been appended, false otherwise.
*/
_appendFeedReaderNotification:
function WCCR__appendFeedReaderNotification(aURI, aName, aNotificationBox) {
var uriSpec = aURI.spec;
var notificationValue = "feed reader notification: " + uriSpec;
var notificationIcon = aURI.prePath + "/favicon.ico";
// Don't append a new notification if the notificationbox
// has a notification for the given feed reader already
if (aNotificationBox.getNotificationWithValue(notificationValue))
return false;
var buttons, message;
if (this.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uriSpec))
message = this._getFormattedString("handlerRegistered", [aName]);
else {
message = this._getFormattedString("addHandler", [aName, aURI.host]);
var self = this;
var addButton = {
_outer: self,
label: self._getString("addHandlerAddButton"),
accessKey: self._getString("addHandlerAddButtonAccesskey"),
feedReaderInfo: { uri: uriSpec, name: aName },
/* static */
callback:
function WCCR__addFeedReaderButtonCallback(aNotification, aButtonInfo) {
var uri = aButtonInfo.feedReaderInfo.uri;
var name = aButtonInfo.feedReaderInfo.name;
var outer = aButtonInfo._outer;
// The reader could have been added from another window mean while
if (!outer.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uri)) {
outer._registerContentHandler(TYPE_MAYBE_FEED, uri, name);
outer._saveContentHandlerToPrefs(TYPE_MAYBE_FEED, uri, name);
// Make the new handler the last-selected reader in the preview page
// and make sure the preview page is shown the next time a feed is visited
var pb = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).getBranch(null);
pb.setCharPref(PREF_SELECTED_READER, "web");
var supportsString =
Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
supportsString.data = uri;
pb.setComplexValue(PREF_SELECTED_WEB, Ci.nsISupportsString,
supportsString);
pb.setCharPref(PREF_SELECTED_ACTION, "ask");
outer._setAutoHandler(TYPE_MAYBE_FEED, null);
}
// avoid reference cycles
aButtonInfo._outer = null;
return false;
}
};
buttons = [addButton];
}
aNotificationBox.appendNotification(message,
notificationValue,
notificationIcon,
aNotificationBox.PRIORITY_INFO_LOW,
buttons);
return true;
},
/**
* Save Web Content Handler metadata to persistent preferences.
* @param contentType

View File

@ -1,10 +0,0 @@
<!ENTITY addFeedReader.title
"Add Feed Reader">
<!ENTITY addAsDefault.label
"Make this my default feed reader">
<!ENTITY addAsDefault.accesskey
"M">
<!ENTITY forSite.label
"Site:">
<!ENTITY feedReaderRegistered.title
"Feed Reader Already Registered">

View File

@ -1,7 +1,7 @@
linkTitleTextFormat=Go to %S
addHandler=Add "%S" as a feed reader?
addHandlerYes=Yes
addHandlerNo=No
addHandler=Add "%S" (%S) as a Feed Reader?
addHandlerAddButton=Add Feed Reader
addHandlerAddButtonAccesskey=A
handlerRegistered="%S" is already registered as a Feed Reader
liveBookmarks=Live Bookmarks
subscribeNow=Subscribe Now

View File

@ -46,7 +46,6 @@
locale/browser/safebrowsing/report-phishing.dtd (%chrome/browser/safebrowsing/report-phishing.dtd)
#endif
locale/browser/feeds/subscribe.dtd (%chrome/browser/feeds/subscribe.dtd)
locale/browser/feeds/addFeedReader.dtd (%chrome/browser/feeds/addFeedReader.dtd)
locale/browser/feeds/subscribe.properties (%chrome/browser/feeds/subscribe.properties)
locale/browser/history/history.dtd (%chrome/browser/history/history.dtd)
locale/browser/migration/migration.dtd (%chrome/browser/migration/migration.dtd)

View File

@ -1,17 +0,0 @@
#imageBox {
width: 32px;
height: 32px;
background-image: url("chrome://browser/skin/feeds/feedIcon.png");
background-repeat: no-repeat;
background-position: 0 0;
margin: .5em;
}
#content {
margin-top: 0.4em;
}
#siteLabel {
margin-top: 3px;
}

View File

@ -37,7 +37,6 @@ 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/addFeedReader.css (feeds/addFeedReader.css)
#ifdef MOZ_PLACES
skin/classic/browser/places/places.css (places/places.css)
skin/classic/browser/places/defaultFavicon.png (places/defaultFavicon.png)

View File

@ -1,17 +0,0 @@
#imageBox {
width: 32px;
height: 32px;
background-image: url("chrome://browser/skin/feeds/feedIcon.png");
background-repeat: no-repeat;
background-position: 0 0;
margin: .5em;
}
#content {
margin-top: 0.4em;
}
#siteLabel {
margin-top: 3px;
}

View File

@ -46,7 +46,6 @@ classic.jar:
skin/classic/browser/feeds/feedIcon.png (feeds/feedIcon.png)
skin/classic/browser/feeds/feedIcon16.png (feeds/feedIcon16.png)
skin/classic/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/browser/feeds/addFeedReader.css (feeds/addFeedReader.css)
#ifdef MOZ_PLACES
skin/classic/browser/places/places.css (places/places.css)
skin/classic/browser/places/defaultFavicon.png (places/defaultFavicon.png)