Bug 592436 - Allow badging items in the awesomebar [r=mfinkle]
--- a/mobile/chrome/content/bindings.xml
+++ b/mobile/chrome/content/bindings.xml
@@ -70,21 +70,25 @@
linkURL: url
}
};
ContextHelper.showPopup(data);
]]>
</handler>
</handlers>
<content orient="vertical">
- <xul:hbox class="autocomplete-item-label" align="top" xbl:inherits="tags, favorite" mousethrough="always">
+ <xul:hbox class="autocomplete-item-container" align="top" xbl:inherits="favorite" mousethrough="always">
<xul:image xbl:inherits="src"/>
<xul:vbox flex="1">
- <xul:label crop="center" xbl:inherits="value"/>
- <xul:label class="autocomplete-item-url" xbl:inherits="value=url" crop="center" mousethrough="always"/>
+ <xul:label class="autocomplete-item-label" crop="center" xbl:inherits="value"/>
+ <xul:label class="autocomplete-item-url" xbl:inherits="value=url" crop="center"/>
+ </xul:vbox>
+ <xul:vbox align="end">
+ <xul:label class="autocomplete-item-tags" value="" xbl:inherits="value=tags"/>
+ <xul:label class="autocomplete-item-badge" value="" xbl:inherits="value=badge"/>
</xul:vbox>
</xul:hbox>
</content>
</binding>
<binding id="popup_autocomplete">
<content orient="vbox" class="autocomplete-box" flex="1">
<!-- 24 child items, to match browser.urlbar.maxRichResults -->
@@ -125,16 +129,21 @@
<property name="boxObject"
readonly="true"
onget="return this._items.boxObject;"/>
<field name="_scrollBoxObject">
this.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
</field>
+ <!-- Used by the badges implementation -->
+ <field name="_badges">[]</field>
+ <field name="_badgesTimeout">-1</field>
+ <field name="_eTLDService">Cc["@mozilla.org/network/effective-tld-service;1"].getService(Ci.nsIEffectiveTLDService);</field>
+
<!-- nsIAutocompleteInput -->
<property name="overrideValue"
readonly="true"
onget="return null;"/>
<field name="_input"/>
<property name="input"
readonly="true"
@@ -253,60 +262,68 @@
if (i > matchCount - 1) {
// Just clear out the old item's value. CSS takes care of hiding
// everything else based on value="" (star, tags, etc.)
item.setAttribute("value", "");
item._empty = true;
continue;
}
- item._empty = false;
// Assign the values
let type = controller.getStyleAt(i);
let title = controller.getCommentAt(i);
let tags = '';
if (type == "tag") {
try {
[, title, tags] = title.match(/^(.+) \u2013 (.+)$/);
} catch (e) {}
}
item.setAttribute("tags", tags);
let url = controller.getValueAt(i);
item.setAttribute("value", title || url);
- item.setAttribute("url", url);
+
+ // remove the badge only if the url has changed
+ if (item._empty || item.getAttribute("url") != url) {
+ item.setAttribute("url", url);
+ item.removeAttribute("badge");
+ }
let isBookmark = ((type == "bookmark") || (type == "tag"));
item.setAttribute("favorite", isBookmark);
item.setAttribute("src", controller.getImageAt(i));
+
+ item._empty = false;
}
// Show the "no results" or "all bookmarks" entries as needed
this._updateNoResultsItem(matchCount);
// Make sure the list is scrolled to the top
this.scrollToTop();
+ this._invalidateBadges();
]]></body>
</method>
<method name="_updateNoResultsItem">
<parameter name="isResults" />
<body><![CDATA[
let noResultsItem = this._items.childNodes.item(1);
if (isResults) {
noResultsItem.className = "";
} else {
noResultsItem.className = "noresults";
noResultsItem.setAttribute("value", "]]>&noResults.label;<![CDATA[");
noResultsItem.removeAttribute("favorite");
noResultsItem.removeAttribute("url");
noResultsItem.removeAttribute("src");
noResultsItem.removeAttribute("tags");
+ noResultsItem.removeAttribute("badge");
}
]]></body>
</method>
<method name="selectBy">
<parameter name="aReverse"/>
<parameter name="aPage"/>
<body><![CDATA[
@@ -323,16 +340,74 @@
newIndex = 0;
else if (newIndex < 0)
newIndex = lastIndex;
this.selectedIndex = newIndex;
]]></body>
</method>
+ <method name="_getEffectiveHost">
+ <parameter name="aURI"/>
+ <body><![CDATA[
+ let host = null;
+ try {
+ host = aURI.host;
+ return this._eTLDService.getBaseDomainFromHost(host);
+ } catch (e) {}
+
+ return host ? host : aURI.spec;
+ ]]></body>
+ </method>
+
+ <method name="registerBadgeHandler">
+ <parameter name="aURL"/>
+ <parameter name="aHandler"/>
+ <body><![CDATA[
+ if (!aHandler)
+ return false;
+
+ let effectiveHost = this._getEffectiveHost(Services.io.newURI(aURL, null, null));
+ this._badges[effectiveHost] = aHandler;
+ return true;
+ ]]></body>
+ </method>
+
+ <method name="unregisterBagdeHandler">
+ <parameter name="aURL"/>
+ <body><![CDATA[
+ let effectiveHost = this._getEffectiveHost(Services.io.newURI(aURL, null, null));
+ if (this._badges[effectiveHost])
+ delete this._badges[effectiveHost];
+ ]]></body>
+ </method>
+
+ <method name="_invalidateBadges">
+ <body><![CDATA[
+ window.clearTimeout(this._badgesTimeout);
+
+ this._badgesTimeout = window.setTimeout(function(self) {
+ for (let i = 0; i < self._items.childNodes.length; i++) {
+ let item = self._items.childNodes[i];
+ if (!item.hasAttribute("url"))
+ continue;
+
+ let itemHost = self._getEffectiveHost(Services.io.newURI(item.getAttribute("url"), null, null));
+ for (let badgeHost in self._badges) {
+ if (itemHost == badgeHost) {
+ let handler = self._badges[badgeHost];
+ handler.updateBadge ? handler.updateBadge(item) : handler(item);
+ break;
+ }
+ }
+ }
+ }, 300, this);
+ ]]></body>
+ </method>
+
<!-- Helpers -->
<field name="_items">
document.getAnonymousElementByAttribute(this,
"anonid", "autocomplete-items");
</field>
<property name="_matchCount"
readonly="true">
@@ -615,22 +690,25 @@
]]>
</body>
</method>
</implementation>
</binding>
<binding id="place-item" extends="chrome://browser/content/bindings.xml#place-base">
<content orient="vertical">
- <xul:hbox anonid="bookmark-item" class="bookmark-item-label" align="top" flex="1" xbl:inherits="tags" mousethrough="always">
+ <xul:hbox anonid="bookmark-item" class="bookmark-item-container" align="top" flex="1" mousethrough="always">
<xul:image xbl:inherits="src"/>
<xul:vbox flex="1">
- <xul:label crop="center" xbl:inherits="value=title"/>
+ <xul:label class="bookmark-item-label" crop="center" xbl:inherits="value=title"/>
<xul:label anonid="bookmark-url" class="bookmark-item-url" xbl:inherits="value=uri" crop="center" mousethrough="always"/>
</xul:vbox>
+ <xul:vbox>
+ <xul:label class="bookmark-item-tags" xbl:inherits="value=tags"/>
+ </xul:vbox>
</xul:hbox>
<xul:hbox anonid="bookmark-manage" class="bookmark-manage" hidden="true" flex="1">
<xul:image xbl:inherits="src"/>
<xul:vbox flex="1">
<xul:vbox flex="1">
<xul:textbox anonid="name" xbl:inherits="value=title"/>
<xul:textbox anonid="uri" xbl:inherits="value=uri"/>
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -2428,17 +2428,16 @@ var ContextCommands = {
},
removeBookmark: function cc_removeBookmark() {
let target = ContextHelper.popupState.target;
target.remove();
}
}
-
var SharingUI = {
_dialog: null,
show: function show(aURL, aTitle) {
this._dialog = importDialog(window, "chrome://browser/content/share.xul", null);
document.getElementById("share-title").value = aTitle || aURL;
BrowserUI.pushPopup(this, this._dialog);
--- a/mobile/themes/core/browser.css
+++ b/mobile/themes/core/browser.css
@@ -669,17 +669,17 @@ placeitem[type="folder"] {
placeitem[type="folder"]:-moz-locale-dir(rtl) {
background: url(images/arrowleft-16.png) no-repeat 2% 50%;
}
placelist[ui="manage"] placeitem[type="folder"] {
background-image: none;
}
-placeitem[type="folder"] > .bookmark-item-label > image,
+placeitem[type="folder"] > .bookmark-item-container > image,
placeitem[type="folder"] > .bookmark-manage > image {
list-style-image: url(images/folder-32.png);
margin-top: 0;
}
placeitem[type="folder"] .bookmark-item-url {
display: none;
}
@@ -757,89 +757,120 @@ placeitem {
autocompleteresult:active,
placelist placeitem:active:not([selected="true"]),
historylist placeitem:active:not([selected="true"]):not([class="history-item-title"]),
historylist placeitem:active:not([selected="true"]):not([class="remotetabs-item-title"]),
.autocompleteresult-selected {
background-color: #8db8d8;
}
+.autocomplete-item-container,
+.bookmark-item-container {
+ margin: 0;
+ padding: 0;
+}
+
.autocomplete-item-label,
.bookmark-item-label {
- margin: 0;
- padding: 0;
font-size: 24px !important;
font-weight: normal;
-moz-margin-end: 8px;
}
-.autocomplete-item-label > image,
-.bookmark-item-label > image,
+.autocomplete-item-container > image,
+.bookmark-item-container > image,
placeitem > .bookmark-manage > image {
width: 32px;
height: 32px;
max-height: 32px;
/* margin-top = (1 - title's line-height) * title's font-size */
margin-top: 5px;
margin-bottom: 0;
-moz-margin-end: 16px;
-moz-margin-start: 8px;
}
-.autocomplete-item-label > image[src=""],
-placeitem[src=""] .bookmark-item-label > image {
+.autocomplete-item-container > image[src=""],
+placeitem[src=""] .bookmark-item-container > image {
list-style-image: url(chrome://mozapps/skin/places/defaultFavicon.png);
}
-.autocomplete-item-label > vbox > label,
-.bookmark-item-label > vbox > label {
+.autocomplete-item-container > vbox > label,
+.bookmark-item-container > vbox > label {
-moz-margin-start: 1px;
}
-.autocomplete-item-label[favorite="true"] {
- -moz-padding-end: 30px;
+.autocomplete-item-container[favorite="true"] {
background: url(images/star-24.png) no-repeat 100% 2px;
}
-.autocomplete-item-label[favorite="true"]:-moz-locale-dir(rtl) {
+.autocomplete-item-container[favorite="true"]:-moz-locale-dir(rtl) {
background: url(images/star-24.png) no-repeat left 2px;
}
-.autocomplete-item-label:not([tags=""]):after,
-.bookmark-item-label:not([tags=""]):after {
- float: right;
- content: attr(tags);
- font-size: 18px !important;
- font-weight: lighter;
- padding-top: 4px;
- -moz-margin-start: 8px;
-}
-
.autocomplete-item-url,
.bookmark-item-url {
color: blue;
font-size: 18px !important;
-moz-margin-end: 24px;
}
+.autocomplete-item-container[favorite="true"] .autocomplete-item-label {
+ -moz-padding-end: 30px;
+}
+
+.autocomplete-item-tags,
+.bookmark-item-tags {
+ content: attr(tags);
+ font-size: 18px !important;
+ font-weight: lighter;
+ margin: 2px 0 4px 0px;
+ -moz-margin-start: 8px;
+ -moz-padding-end: 32px;
+}
+
+.autocomplete-item-tags[value=""] {
+ visibility: hidden;
+}
+
+.autocomplete-item-badge {
+ opacity: 1;
+ -moz-transition: opacity 1s ease;
+ background-color: #c90707;
+ border: 1px solid #951919;
+ -moz-border-radius: 2px;
+ content: attr(badge);
+ font-size: 12px !important;
+ font-weight: bolder;
+ margin: 4px 0 0 0;
+ padding: 4px 6px;
+ color: white;
+}
+
+autocompleteresult:not([badge]) .autocomplete-item-badge,
+.autocomplete-item-badge[value=""] {
+ opacity: 0;
+ -moz-transition: none;
+}
+
/* special "no results", "awesome header row" and "title rows" items */
autocompleteresult[class="history-item-title"],
autocompleteresult[class="remotetabs-item-title"] {
-moz-box-pack: center;
background-color: #E9E9E9;
min-height: 0px;
}
autocompleteresult[class="history-item-title"] .bookmark-item-url,
autocompleteresult[class="remotetabs-item-title"] .bookmark-item-url {
display: none;
}
-autocompleteresult[class="history-item-title"] .bookmark-item-label,
-autocompleteresult[class="remotetabs-item-title"] .bookmark-item-label {
+autocompleteresult[class="history-item-title"] .bookmark-item-container,
+autocompleteresult[class="remotetabs-item-title"] .bookmark-item-container {
font-size: 24px !important;
}
autocompleteresult[class="history-item-title"] image,
autocompleteresult[class="remotetabs-item-title"] image {
display: none;
}
@@ -847,17 +878,17 @@ autocompleteresult.noresults {
font-style: italic;
border-bottom: none;
}
autocompleteresult.noresults:active {
background-color: white;
}
-autocompleteresult.noresults > .autocomplete-item-label {
+autocompleteresult.noresults > .autocomplete-item-container {
text-align: center;
color: grey;
}
#awesome-header {
min-height: 70px; /* row size */
}