Bug 581999 - "Make it easier for add-ons to extend the context menu"
[r=mark.finkle] (pt 2)
missed part - forgot to qrefresh after resolving conflicts
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -2039,47 +2039,41 @@ var ContextHelper = {
this.popupState = aMessage.json;
this.popupState.target = aMessage.target;
let first = null;
let last = null;
let commands = document.getElementById("context-commands");
for (let i=0; i<commands.childElementCount; i++) {
let command = commands.children[i];
+ command.removeAttribute("selector");
+ command.hidden = true;
+
let types = command.getAttribute("type").split(/\s+/);
- command.removeAttribute("selector");
- if ((types.indexOf("image") != -1 && this.popupState.onImage) ||
- (types.indexOf("image-loaded") != -1 && this.popupState.onLoadedImage) ||
- (types.indexOf("link") != -1 && this.popupState.onSaveableLink) ||
- (types.indexOf("callto") != -1 && this.popupState.onVoiceLink) ||
- (types.indexOf("mailto") != -1 && this.popupState.onLink && this.popupState.linkProtocol == "mailto") ||
- (types.indexOf("edit-bookmark") != -1 && this.popupState.onBookmark)) {
- first = (first ? first : command);
- last = command;
- command.hidden = false;
- continue;
+ for (let i=0; i<types.length; i++) {
+ if (this.popupState.types.indexOf(types[i]) != -1) {
+ first = first || command;
+ last = command;
+ command.hidden = false;
+ break;
+ }
}
- command.hidden = true;
}
if (!first) {
this.popupState = null;
return;
}
+ // Allow the first and last *non-hidden* elements to be selected in CSS.
first.setAttribute("selector", "first-child");
last.setAttribute("selector", "last-child");
let label = document.getElementById("context-hint");
- if (this.popupState.onImage)
- label.value = this.popupState.mediaURL;
- if (this.popupState.onLink)
- label.value = this.popupState.linkURL;
- if (this.popupState.onBookmark)
- label.value = this.popupState.bookmarkURL;
+ label.value = this.popupState.label;
this._panel.hidden = false;
window.addEventListener("resize", this, true);
this.sizeToContent();
BrowserUI.pushPopup(this, [this._popup]);
},
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -510,17 +510,17 @@
</vbox>
<hbox id="context-container" class="window-width window-height context-block" top="0" left="0" hidden="true">
<vbox id="context-popup" class="dialog-dark">
<hbox id="context-header">
<label id="context-hint" crop="center" flex="1"/>
</hbox>
<richlistbox id="context-commands" onclick="ContextHelper.hide();">
- <richlistitem id="context-openinnewtab" type="link" onclick="ContextCommands.openInNewTab(event);">
+ <richlistitem id="context-openinnewtab" type="link-saveable" onclick="ContextCommands.openInNewTab(event);">
<label value="&contextOpenInNewTab.label;"/>
</richlistitem>
<richlistitem id="context-saveimage" type="image-loaded" onclick="ContextCommands.saveImage(event);">
<label value="&contextSaveImage.label;"/>
</richlistitem>
<richlistitem id="context-editbookmark" type="edit-bookmark" onclick="ContextCommands.editBookmark(event);">
<label value="&contextEditBookmark.label;"/>
</richlistitem>
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -739,88 +739,109 @@ var ContextHandler = {
},
_getProtocol: function ch_getProtocol(aURI) {
if (aURI)
return aURI.scheme;
return null;
},
- _isSaveable: function ch_isSaveable(aProtocol) {
- // We don't do the Right Thing for news/snews yet, so turn them off until we do
- return aProtocol && !(aProtocol == "mailto" || aProtocol == "javascript" || aProtocol == "news" || aProtocol == "snews");
- },
-
- _isVoice: function ch_isVoice(aProtocol) {
- // Collection of protocols related to voice or data links
- return aProtocol && (aProtocol == "tel" || aProtocol == "callto" || aProtocol == "sip" || aProtocol == "voipto");
- },
-
init: function ch_init() {
addEventListener("contextmenu", this, false);
},
handleEvent: function ch_handleEvent(aEvent) {
if (aEvent.getPreventDefault())
return;
let state = {
- onLink: false,
- onSaveableLink: false,
- onVoiceLink: false,
- onImage: false,
- onLoadedImage: false,
+ types: [],
+ label: "",
linkURL: "",
linkProtocol: null,
mediaURL: ""
};
let popupNode = aEvent.originalTarget;
// Do checks for nodes that never have children.
if (popupNode.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
// See if the user clicked on an image.
if (popupNode instanceof Ci.nsIImageLoadingContent && popupNode.currentURI) {
- state.onImage = true;
+ state.types.push("image");
state.mediaURL = popupNode.currentURI.spec;
-
- let request = popupNode.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
- if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
- state.onLoadedImage = true;
+ state.label = state.mediaURL;
}
}
let elem = popupNode;
while (elem) {
if (elem.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
// Link?
- if (!this.onLink &&
- ((elem instanceof Ci.nsIDOMHTMLAnchorElement && elem.href) ||
- (elem instanceof Ci.nsIDOMHTMLAreaElement && elem.href) ||
- elem instanceof Ci.nsIDOMHTMLLinkElement ||
- elem.getAttributeNS(kXLinkNamespace, "type") == "simple")) {
+ if ((elem instanceof Ci.nsIDOMHTMLAnchorElement && elem.href) ||
+ (elem instanceof Ci.nsIDOMHTMLAreaElement && elem.href) ||
+ elem instanceof Ci.nsIDOMHTMLLinkElement ||
+ elem.getAttributeNS(kXLinkNamespace, "type") == "simple") {
// Target is a link or a descendant of a link.
- state.linkURL = this._getLinkURL(elem);
+ state.types.push("link");
+ state.label = state.linkURL = this._getLinkURL(elem);
state.linkProtocol = this._getProtocol(this._getURI(state.linkURL));
- state.onLink = true;
- state.onSaveableLink = this._isSaveable(state.linkProtocol);
- state.onVoiceLink = this._isVoice(state.linkProtocol);
+ break;
}
}
elem = elem.parentNode;
}
+
+ for (let i = 0; i < this._types.length; i++)
+ if (this._types[i].handler(state, popupNode))
+ state.types.push(this._types[i].name);
sendAsyncMessage("Browser:ContextMenu", state);
+ },
+
+ /**
+ * For add-ons to add new types and data to the ContextMenu message.
+ *
+ * @param aName A string to identify the new type.
+ * @param aHandler A function that takes a state object and a target element.
+ * If aHandler returns true, then aName will be added to the list of types.
+ * The function may also modify the state object.
+ */
+ registerType: function registerType(aName, aHandler) {
+ this._types.push({name: aName, handler: aHandler});
}
};
ContextHandler.init();
+ContextHandler.registerType("mailto", function(aState, aElement) {
+ return aState.linkProtocol == "mailto";
+});
+
+ContextHandler.registerType("callto", function(aState, aElement) {
+ let protocol = aState.linkProtocol;
+ return protocol == "tel" || protocol == "callto" || protocol == "sip" || protocol == "voipto";
+});
+
+ContextHandler.registerType("link-saveable", function(aState, aElement) {
+ let protocol = aState.linkProtocol;
+ dump(protocol+"\n");
+ return (protocol && protocol != "mailto" && protocol != "javascript" && protocol != "news" && protocol != "snews");
+});
+
+ContextHandler.registerType("image-loaded", function(aState, aElement) {
+ if (aState.types.indexOf("image") != -1) {
+ let request = aElement.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
+ if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
+ return true;
+ }
+ return false;
+});
var FormSubmitObserver = {
init: function init(){
addMessageListener("Browser:TabOpen", this);
addMessageListener("Browser:TabClose", this);
},