gecko/mobile/chrome/content/bindings.xml

1497 lines
50 KiB
XML

<?xml version="1.0"?>
<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="autocomplete-aligned" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
<implementation>
<property name="mIgnoreFocus" onget="return true;" onset="val;"/>
<method name="openPopup">
<body><![CDATA[
this.popup.openAutocompletePopup(this, null);
]]></body>
</method>
<method name="closePopup">
<body><![CDATA[
// hack! we want to revert to the "all results" popup when the
// controller would otherwise close us because of an empty search
// string.
if (this.value == "")
this.showHistoryPopup();
]]></body>
</method>
</implementation>
<handlers>
<handler event="keypress" phase="capturing">
<![CDATA[
if (event.keyCode == event.DOM_VK_RETURN)
setTimeout(function() { BrowserUI.activePanel = null }, 0);
else if (this.readOnly)
this.readOnly = false;
]]>
</handler>
<handler event="text" phase="bubbling">
<![CDATA[
if (this.mController.input == this)
this.mController.handleText();
]]>
</handler>
<handler event="blur" phase="capturing">
<![CDATA[
// Bug 583341 - suppress disconnect of autocomplete controller
this._dontBlur = true;
]]>
</handler>
</handlers>
</binding>
<binding id="popup_autocomplete_result">
<handlers>
<handler event="contextmenu" phase="capturing">
<![CDATA[
let url = this.getAttribute("url");
if (!url)
return;
let value = this.getAttribute("value");
let data = {
target: this,
json: {
types: ["link-saveable", "link-shareable"],
label: value,
linkTitle: value,
linkURL: url
}
};
ContextHelper.showPopup(data);
]]>
</handler>
</handlers>
<content orient="vertical">
<xul:hbox class="autocomplete-item-container" align="top" xbl:inherits="favorite,remote" mousethrough="always">
<xul:image xbl:inherits="src"/>
<xul:vbox flex="1">
<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 -->
<xul:scrollbox orient="vertical"
class="autocomplete-items"
anonid="autocomplete-items"
flex="1000">
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
<xul:autocompleteresult/>
</xul:scrollbox>
<children/>
</content>
<implementation implements="nsIAutoCompletePopup">
<!-- Used by the chrome input handler -->
<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"
onget="return this._input;"/>
<field name="_selectedIndex">-1</field>
<field name="_selectedItem"/>
<property name="selectedIndex" onget="return this._selectedIndex;">
<setter><![CDATA[
// Ignore invalid indices
if (val < -1 ||
val > this._matchCount - 1)
return val;
if (this._selectedItem)
this._styleItem(this._selectedItem, false);
// highlight the selected item
let item = this._items.childNodes.item(val);
if (item) {
this._selectedItem = item;
this._styleItem(this._selectedItem, true);
this._scrollBoxObject.ensureElementIsVisible(this._selectedItem);
}
return this._selectedIndex = val;
]]></setter>
</property>
<field name="_popupOpen">false</field>
<property name="popupOpen"
readonly="true"
onget="return this._popupOpen;"/>
<method name="openAutocompletePopup">
<parameter name="aInput"/>
<parameter name="aElement"/>
<body><![CDATA[
if (this._popupOpen)
return;
this._selectedItem = null;
this._input = aInput;
this._input.select();
this._popupOpen = true;
this.invalidate();
let event = document.createEvent("Events");
event.initEvent("popupshown", true, false);
this.dispatchEvent(event);
]]></body>
</method>
<method name="closePopup">
<body><![CDATA[
if (!this._popupOpen)
return;
this.selectedIndex = -1;
this.input.controller.stopSearch();
this._popupOpen = false;
let event = document.createEvent("Events");
event.initEvent("popuphidden", true, false);
this.dispatchEvent(event);
]]></body>
</method>
<method name="scrollToTop">
<body><![CDATA[
if (this._items.scrollTop || this._items.scrollLeft)
this._scrollBoxObject.scrollTo(0, 0);
]]></body>
</method>
<!-- Helper used by active dialog system -->
<method name="close">
<body><![CDATA[
this.input.reset();
this.input.blur();
this.closePopup();
]]></body>
</method>
<field name="_XULNS">("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")</field>
<method name="invalidate">
<body><![CDATA[
// Don't bother doing work if we're not even open
if (!this.popupOpen)
return;
let controller = this.input.controller;
let searchString = controller.searchString;
let items = this._items;
// Need to iterate over all our existing entries at a minimum, to make
// sure they're either updated or cleared out. We might also have to
// add extra items.
let matchCount = this._matchCount;
let children = items.childNodes;
let iterCount = Math.max(children.length, matchCount);
for (let i = 0; i < iterCount; ++i) {
let item = children.item(i);
// Create an item if needed
if (!item) {
item = document.createElementNS(this._XULNS, "xul:autocompleteresult");
items.appendChild(item);
}
item._index = i;
// Check whether there's an entry to fill
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;
}
// 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);
// remove the badge only if the url has changed
if (item._empty || item.getAttribute("url") != url) {
item.setAttribute("url", url);
item.removeAttribute("badge");
item.removeAttribute("remote");
}
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");
noResultsItem.removeAttribute("remote");
}
]]></body>
</method>
<method name="selectBy">
<parameter name="aReverse"/>
<parameter name="aPage"/>
<body><![CDATA[
let newIndex;
let lastIndex = this._matchCount - 1;
if (this._selectedIndex == -1)
newIndex = aReverse ? lastIndex : 0;
else
newIndex = this._selectedIndex + (aReverse ? -1 : 1);
// Deal with rollover
if (newIndex > lastIndex)
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) {
#ifdef MOZ_SERVICES_SYNC
let remoteItems = RemoteTabsList.panel._getRemoteTabs();
#endif
for (let i = 0; i < self._items.childNodes.length; i++) {
let item = self._items.childNodes[i];
if (!item.hasAttribute("url"))
continue;
let currentURL = item.getAttribute("url");
let itemHost = self._getEffectiveHost(Services.io.newURI(currentURL, null, null));
for (let badgeHost in self._badges) {
if (itemHost == badgeHost) {
// wrap the item to prevent setting a badge on a wrong item
let wrapper = {
set: function(aBadge) {
if (item.getAttribute("url") != currentURL)
return;
if (!aBadge || aBadge == "")
item.removeAttribute("badge");
else
item.setAttribute("badge", aBadge);
}
};
let handler = self._badges[badgeHost];
handler.updateBadge ? handler.updateBadge(wrapper) : handler(wrapper);
break;
}
}
#ifdef MOZ_SERVICES_SYNC
// once we have check badges, check if the tab is in the remote list
if (!Weave.Service.isLoggedIn)
return;
for (let i = 0; i < remoteItems.length; i++) {
let remoteURI = remoteItems[i].uri;
if (remoteURI) {
let itemHost = Services.io.newURI(currentURL, null, null);
let remoteHost = Services.io.newURI(remoteURI, null, null);
if (itemHost.equals(remoteHost))
item.setAttribute("remote", "true");
}
}
#endif
}
}, 300, this);
]]></body>
</method>
<!-- Helpers -->
<field name="_items">
document.getAnonymousElementByAttribute(this,
"anonid", "autocomplete-items");
</field>
<property name="_matchCount"
readonly="true">
<getter><![CDATA[
return this.input.controller.matchCount;
]]></getter>
</property>
<method name="_styleItem">
<parameter name="aItem"/>
<parameter name="aAddStyle"/>
<body><![CDATA[
if (aAddStyle)
aItem.className += " autocompleteresult-selected";
else
aItem.className = aItem.className.replace(/\s*autocompleteresult-selected/, "");
]]></body>
</method>
</implementation>
<handlers>
<handler event="click" button="0">
<![CDATA[
let target = event.originalTarget;
if (target.localName == "autocompleteresult" && !target._empty) {
this._selectedIndex = target._index;
this.input.controller.handleEnter(true);
BrowserUI.activePanel = null;
}
]]>
</handler>
</handlers>
</binding>
<binding id="place-base">
<content/>
<handlers>
<handler event="click" button="0">
<![CDATA[
if (this.control)
this.control._fireOpen(event, this);
]]>
</handler>
<handler event="contextmenu" phase="capturing">
<![CDATA[
if (!this.uri || this._isEditing)
return;
let data = {
target: this,
json: {
types: ["edit-bookmark", "link-saveable", "link-shareable"],
label: this.name,
linkTitle: this.name,
linkURL: this.spec
}};
ContextHelper.showPopup(data);
]]>
</handler>
</handlers>
<implementation>
<field name="_uri">null</field>
<field name="_control">null</field>
<field name="_isEditing">false</field>
<field name="_nameField">
document.getAnonymousElementByAttribute(this, "anonid", "name");
</field>
<field name="_uriField">
document.getAnonymousElementByAttribute(this, "anonid", "uri");
</field>
<field name="_tagsField">
document.getAnonymousElementByAttribute(this, "anonid", "tags");
</field>
<property name="itemId" onget="return this.getAttribute('itemid');"/>
<property name="type" onget="return this.getAttribute('type');"/>
<property name="uri">
<getter><![CDATA[
if (!this._uri && this.getAttribute("uri"))
this._uri = Services.io.newURI(this.getAttribute("uri"), null, null);
return this._uri;
]]></getter>
</property>
<property name="name" onget="return this._nameField.value"
onset="this._nameField.value = val; return val;"/>
<property name="spec" onget="return this._uriField.value"
onset="this._uriField.value = val; return val;"/>
<property name="tags" onget="return this._tagsField.value"
onset="this._tagsField.value = val; return val;"/>
<property name="tagsAsArray" readonly="true">
<getter>
<![CDATA[
// we don't require the leading space (after each comma)
var tags = this.tags.split(",");
for (var i = 0; i < tags.length; i++) {
// remove trailing and leading spaces
tags[i] = tags[i].trim();
// remove empty entries from the array.
if (tags[i] == "") {
tags.splice(i, 1);
i--;
}
}
return tags;
]]>
</getter>
</property>
<property name="isEditing" readonly="true" onget="return this._isEditing;"/>
<property name="isReadOnly" readonly="true">
<getter><![CDATA[
return this.control && this.control._readOnlyFolders.indexOf(parseInt(this.itemId, 10)) != -1;
]]></getter>
</property>
<property name="control" readonly="true">
<getter>
<![CDATA[
if (this._control)
return this._control;
let parent = this.parentNode;
while (parent) {
if (parent.localName == "placelist") {
this._control = parent;
return this._control;
}
parent = parent.parentNode;
}
return null;
]]>
</getter>
</property>
<method name="startEditing">
<parameter name="autoSelect"/>
<body>
<![CDATA[
if (!this.itemId || this.isReadOnly)
return;
this._isEditing = true;
if (this.control) {
this.setAttribute("selected", "true");
this.control.scrollBoxObject.ensureElementIsVisible(this);
this.control.activeItem = this;
}
this.updateFields();
this._nameField.focus();
if (autoSelect)
this._nameField.select();
]]>
</body>
</method>
<method name="stopEditing">
<parameter name="shouldSave"/>
<body>
<![CDATA[
if (!this.itemId || this.isReadOnly)
return;
if (shouldSave)
this.save();
this._isEditing = false;
if (this.control && this.control.activeItem) {
this.control.activeItem.removeAttribute("selected");
this.control.activeItem = null;
}
this.updateFields();
let focusedElement = document.commandDispatcher.focusedElement;
if (focusedElement)
focusedElement.blur();
let event = document.createEvent("Events");
event.initEvent("close", false, false);
this.dispatchEvent(event);
]]>
</body>
</method>
<method name="save">
<body>
<![CDATA[
// Update the tags
if (this.uri && this.type != "folder") {
let currentTags = PlacesUtils.tagging.getTagsForURI(this.uri, {});
let tags = this.tagsAsArray;
if (tags.length > 0 || currentTags.length > 0) {
let tagsToRemove = [];
let tagsToAdd = [];
for (let i = 0; i < currentTags.length; i++) {
if (tags.indexOf(currentTags[i]) == -1)
tagsToRemove.push(currentTags[i]);
}
for (let i = 0; i < tags.length; i++) {
if (currentTags.indexOf(tags[i]) == -1)
tagsToAdd.push(tags[i]);
}
if (tagsToAdd.length > 0)
PlacesUtils.tagging.tagURI(this.uri, tagsToAdd);
if (tagsToRemove.length > 0)
PlacesUtils.tagging.untagURI(this.uri, tagsToRemove);
}
this.setAttribute('tags', this.tags);
// If the URI was updated change it in the bookmark, but don't
// allow a blank URI. Revert to previous URI if blank.
let spec = this.spec;
if (spec && this.uri.spec != spec) {
try {
let oldURI = this._uri;
this._uri = Services.io.newURI(spec, null, null);
PlacesUtils.bookmarks.changeBookmarkURI(this.itemId, this.uri);
this.setAttribute('uri', this.spec);
// move tags from old URI to new URI
let tags = this.tagsAsArray;
if (tags.length != 0) {
// only untag the old URI if this is the only bookmark
if (PlacesUtils.getBookmarksForURI(oldURI, {}).length == 0)
PlacesUtils.tagging.untagURI(oldURI, tags);
PlacesUtils.tagging.tagURI(this._uri, tags);
}
}
catch (e) { }
}
if (spec != this.uri.spec)
this.spec = this.uri.spec;
}
// Update the name and use the URI if name is blank
this.name = this.name || this.spec;
this.setAttribute('title', this.name);
PlacesUtils.bookmarks.setItemTitle(this.itemId, this.name);
]]>
</body>
</method>
<method name="remove">
<body>
<![CDATA[
PlacesUtils.bookmarks.removeItem(this.itemId);
// If this was the last bookmark (excluding tag-items and livemark
// children, see getMostRecentBookmarkForURI) for the bookmark's url,
// remove the url from tag containers as well.
if (this.uri && this.type != "folder") {
if (PlacesUtils.getMostRecentBookmarkForURI(this.uri) == -1) {
var tags = PlacesUtils.tagging.getTagsForURI(this.uri, {});
PlacesUtils.tagging.untagURI(this.uri, tags);
}
}
this.stopEditing(false);
let event = document.createEvent("Events");
event.initEvent("BookmarkRemove", true, false);
this.dispatchEvent(event);
if (this.control)
this.control.removeItem(this);
]]>
</body>
</method>
<method name="updateFields">
<body>
<![CDATA[
// implemented by sub classes
]]>
</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-container" align="top" flex="1" mousethrough="always">
<xul:image xbl:inherits="src"/>
<xul:vbox flex="1">
<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"/>
<xul:textbox anonid="tags" xbl:inherits="value=tags" emptytext="&editBookmarkTags.label;"/>
</xul:vbox>
<xul:hbox class="bookmark-controls" align="center">
<xul:spacer flex="1"/>
<xul:button anonid="done-button" class="bookmark-done" label="&editBookmarkDone.label;"
oncommand="document.getBindingParent(this).stopEditing(true)"/>
</xul:hbox>
</xul:vbox>
</xul:hbox>
</content>
<implementation>
<method name="updateFields">
<body>
<![CDATA[
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-item").hidden = this._isEditing;
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-url").hidden = this._isEditing;
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-manage").hidden = !this._isEditing;
]]>
</body>
</method>
</implementation>
</binding>
<binding id="place-folder" extends="chrome://browser/content/bindings.xml#place-item">
<implementation>
<method name="updateFields">
<body>
<![CDATA[
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-item").hidden = this._isEditing;
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-url").hidden = this._isEditing;
this._uriField.hidden = true;
this._tagsField.hidden = true;
document.getAnonymousElementByAttribute(this, "anonid", "bookmark-manage").hidden = !this._isEditing;
]]>
</body>
</method>
</implementation>
</binding>
<binding id="place-label" extends="chrome://browser/content/bindings.xml#place-base">
<handlers>
<handler event="click" button="0">
<![CDATA[
if (this.control)
this.control.open(this.previousSibling.itemId);
]]>
</handler>
</handlers>
<content align="center">
<xul:spacer xbl:inherits="width=indent"/>
<xul:image anonid="favicon" class="bookmark-folder-image"/>
<xul:label anonid="name" crop="end" flex="1" xbl:inherits="value=title"/>
</content>
</binding>
<binding id="place-list">
<content orient="vertical" flex="1">
<xul:vbox anonid="parent-items" class="place-list-parents" />
<xul:richlistbox anonid="child-items" class="place-list-children" flex="1" batch="25"/>
</content>
<implementation>
<constructor>
<![CDATA[
this._type = this.getAttribute("type");
this._mode = this.getAttribute("mode");
this._folderParents = {};
this._folderParents[this._desktopFolderId] = this.mobileRoot;
this._folderParents[PlacesUtils.bookmarks.unfiledBookmarksFolder] = this._desktopFolderId;
this._folderParents[PlacesUtils.bookmarksMenuFolderId] = this._desktopFolderId;
this._folderParents[PlacesUtils.toolbarFolderId] = this._desktopFolderId;
]]>
</constructor>
<field name="_desktopFolderId">-1000</field>
<field name="_desktopFolder"><![CDATA[
({
itemId: this._desktopFolderId, tags: "", uri: "",
title: Elements.browserBundle.getString("bookmarkList.desktop"),
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
})
]]></field>
<field name="_desktopChildren"><![CDATA[
[
{
itemId: PlacesUtils.bookmarks.unfiledBookmarksFolder, tags: "", uri: "",
title: PlacesUtils.bookmarks.getItemTitle(PlacesUtils.bookmarks.unfiledBookmarksFolder),
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
},
{
itemId: PlacesUtils.bookmarksMenuFolderId, tags: "", uri: "",
title: PlacesUtils.bookmarks.getItemTitle(PlacesUtils.bookmarksMenuFolderId),
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
},
{
itemId: PlacesUtils.toolbarFolderId, tags: "", uri: "",
title: PlacesUtils.bookmarks.getItemTitle(PlacesUtils.toolbarFolderId),
type: Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER
}
];
]]></field>
<field name="_readOnlyFolders"><![CDATA[
[
this._desktopFolderId,
PlacesUtils.bookmarks.unfiledBookmarksFolder,
PlacesUtils.bookmarksMenuFolderId,
PlacesUtils.toolbarFolderId
];
]]></field>
<field name="_type"/>
<field name="_mode"/>
<field name="_activeItem">null</field>
<field name="_ignoreEditing">false</field>
<field name="_parents">
document.getAnonymousElementByAttribute(this, "anonid", "parent-items");
</field>
<field name="_children">
document.getAnonymousElementByAttribute(this, "anonid", "child-items");
</field>
<field name="scrollBoxObject">this._children.scrollBoxObject</field>
<property name="items" readonly="true" onget="return this._children.childNodes"/>
<field name="mobileRoot"><![CDATA[
PlacesUtils.annotations.getItemsWithAnnotation("mobile/bookmarksRoot", {})[0];
]]></field>
<property name="isRootFolder" readonly="true">
<getter>
<![CDATA[
let currentFolderId = this._parents.lastChild.itemId;
return currentFolderId == this.mobileRoot;
]]>
</getter>
</property>
<property name="activeItem">
<getter>
<![CDATA[
return this._activeItem;
]]>
</getter>
<setter>
<![CDATA[
if (!this._ignoreEditing) {
if (this._activeItem && this._activeItem.isEditing) {
this._ignoreEditing = true;
this._activeItem.stopEditing(false);
this._ignoreEditing = false;
}
this._activeItem = val;
}
return val;
]]>
</setter>
</property>
<method name="isDesktopFolderEmpty">
<body>
<![CDATA[
let options = PlacesUtils.history.getNewQueryOptions();
options.queryType = options.QUERY_TYPE_BOOKMARKS;
let query = PlacesUtils.history.getNewQuery();
let folders = [ PlacesUtils.bookmarks.unfiledBookmarksFolder,
PlacesUtils.bookmarksMenuFolderId,
PlacesUtils.toolbarFolderId
];
query.setFolders(folders, 3);
let result = PlacesUtils.history.executeQuery(query, options);
let rootNode = result.root;
rootNode.containerOpen = true;
let isEmpty = !rootNode.childCount;
rootNode.containerOpen = false;
return isEmpty;
]]>
</body>
</method>
<method name="_getChildren">
<parameter name="aFolder"/>
<body>
<![CDATA[
let items = [];
let options = PlacesUtils.history.getNewQueryOptions();
options.queryType = (this._type == "bookmarks" ? options.QUERY_TYPE_BOOKMARKS : options.QUERY_TYPE_HISTORY);
// Exclude "query" items (e.g. "smart folders")
options.excludeQueries = true;
let query = PlacesUtils.history.getNewQuery();
if (aFolder)
query.setFolders([aFolder], 1);
let result = PlacesUtils.history.executeQuery(query, options);
let rootNode = result.root;
rootNode.containerOpen = true;
let cc = rootNode.childCount;
for (var i=0; i<cc; ++i) {
let node = rootNode.getChild(i);
// Ignore separators
if (node.type == node.RESULT_TYPE_SEPARATOR)
continue;
if (this._mode == "folders" && node.type == node.RESULT_TYPE_FOLDER) {
items.push(node);
}
else if (this._mode == "") {
items.push(node);
}
}
rootNode.containerOpen = false;
return items;
]]>
</body>
</method>
<method name="open">
<parameter name="aRootFolder"/>
<body>
<![CDATA[
aRootFolder = aRootFolder || this.mobileRoot;
this._activeItem = null;
let parents = this._parents;
while (parents.firstChild)
parents.removeChild(parents.firstChild);
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
var self = this;
let folderId = aRootFolder;
do {
let title;
if (folderId == this._desktopFolderId)
title = this._desktopFolder.title;
else
title = PlacesUtils.bookmarks.getItemTitle(folderId);
let parent = document.createElementNS(XULNS, "placelabel");
parent.setAttribute("class", "bookmark-folder");
parent.setAttribute("itemid", folderId);
parent.setAttribute("indent", 0);
parent.setAttribute("title", title);
parents.insertBefore(parent, parents.firstChild);
folderId = this._folderParents[folderId] || PlacesUtils.bookmarks.getFolderIdForItem(folderId);
} while (folderId != PlacesUtils.bookmarks.placesRoot)
let children = this._children;
while (children.lastChild)
children.removeChild(children.lastChild);
children.scrollBoxObject.scrollTo(0, 0);
let items = (aRootFolder == this._desktopFolderId) ? this._desktopChildren.concat()
: this._getChildren(aRootFolder);
if (aRootFolder == this.mobileRoot && !this.isDesktopFolderEmpty())
items.unshift(this._desktopFolder);
children.setItems(items.map(this.createItem));
]]>
</body>
</method>
<method name="createItem">
<parameter name="aItem"/>
<body>
<![CDATA[
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let child = document.createElementNS(XULNS, "placeitem");
child.setAttribute("itemid", aItem.itemId);
child.setAttribute("class", "bookmark-item");
child.setAttribute("title", aItem.title);
child.setAttribute("uri", aItem.uri);
child.setAttribute("tags", aItem.tags);
if (aItem.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER)
child.setAttribute("type", "folder");
else
child.setAttribute("src", aItem.icon);
return child;
]]>
</body>
</method>
<method name="removeItem">
<parameter name="aItem"/>
<body>
<![CDATA[
this._children.removeChild(aItem);
]]>
</body>
</method>
<method name="_fireOpen">
<parameter name="aEvent"/>
<parameter name="aItem"/>
<body>
<![CDATA[
let target = aEvent.originalTarget;
if (target.localName == "button" || this._activeItem == aItem)
return;
if (aItem.type == "folder") {
this.open(aItem.itemId);
} else {
// Force the item to be active
this._activeItem = aItem;
// This is a callback used to forward information to some
// external code [we fire an event & a pseudo attribute event]
let event = document.createEvent("Events");
event.initEvent("BookmarkOpen", true, false);
this.dispatchEvent(event);
let func = new Function("event", this.getAttribute("onopen"));
func.call(this, aEvent);
}
]]>
</body>
</method>
</implementation>
</binding>
<binding id="history-list">
<handlers>
<handler event="click" button="0">
<![CDATA[
let func = new Function("event", this.getAttribute("onopen"));
func.call(this, event);
]]>
</handler>
</handlers>
<content orient="vertical" flex="1">
<xul:richlistbox anonid="child-items" class="history-list-children" flex="1" batch="25"/>
</content>
<implementation>
<method name="open">
<body><![CDATA[
let children = this._children;
while (children.lastChild)
children.removeChild(children.lastChild);
let items = this._getHistory();
children.setItems(items.map(this.createItem));
]]></body>
</method>
<field name="_children">
document.getAnonymousElementByAttribute(this, "anonid", "child-items");
</field>
<field name="scrollBoxObject">this._children.scrollBoxObject</field>
<method name="_getHistory">
<body><![CDATA[
let items = [];
let historyService = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
let query = historyService.getNewQuery();
let options = historyService.getNewQueryOptions();
options.excludeQueries = true;
options.queryType = options.QUERY_TYPE_HISTORY;
options.maxResults = Services.prefs.getIntPref("browser.display.history.maxresults");
options.resultType = options.RESULTS_AS_URI;
options.sortingMode = options.SORT_BY_DATE_DESCENDING;
let result = historyService.executeQuery(query, options);
let rootNode = result.root;
rootNode.containerOpen = true;
let childCount = rootNode.childCount;
// Get the rows title
let titleToday = PlacesUtils.getString("finduri-AgeInDays-is-0");
let titleYesterday = PlacesUtils.getString("finduri-AgeInDays-is-1");
let titleLastWeek = PlacesUtils.getFormattedString("finduri-AgeInDays-last-is", [7]);
let titleOlder = PlacesUtils.getFormattedString("finduri-AgeInDays-isgreater", [7]);
let lastTitle = null;
let msPerDay = 86400000;
let msPerWeek = msPerDay * 7;
let today = new Date(Date.now() - (Date.now() % msPerDay));
for (let i = 0; i < childCount; i++) {
let node = rootNode.getChild(i);
let time = new Date(node.time / 1000); // node.time is microseconds
// Insert a row title if needed
let msDelta = today - time;
if (msDelta < 0 && lastTitle == null) {
lastTitle = titleToday;
items.push({ title: lastTitle });
} else if (msDelta > 0 && msDelta < msPerDay && lastTitle != titleYesterday) {
lastTitle = titleYesterday;
items.push({ title: lastTitle });
} else if (msDelta > msPerDay && msDelta < msPerWeek && lastTitle != titleLastWeek) {
lastTitle = titleLastWeek;
items.push({ title: lastTitle });
} else if (msDelta > msPerWeek && lastTitle != titleOlder) {
lastTitle = titleOlder;
items.push({ title: lastTitle });
}
items.push(node);
}
rootNode.containerOpen = false;
return items;
]]></body>
</method>
<method name="createItem">
<parameter name="aItem"/>
<body>
<![CDATA[
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let child = document.createElementNS(XULNS, "autocompleteresult");
child.setAttribute("value", aItem.title || aItem.uri);
if (!aItem.uri) {
child.setAttribute("class", "history-item-title");
} else {
child.setAttribute("class", "history-item");
child.setAttribute("url", aItem.uri);
child.setAttribute("src", aItem.icon);
}
return child;
]]>
</body>
</method>
</implementation>
</binding>
<binding id="remotetabs-list">
<handlers>
<handler event="click" button="0">
<![CDATA[
let func = new Function("event", this.getAttribute("onopen"));
func.call(this, event);
]]>
</handler>
</handlers>
<content orient="vertical" flex="1">
<xul:richlistbox anonid="child-items" class="remotetabs-list-children" flex="1" batch="25"/>
</content>
<implementation>
<method name="open">
<body><![CDATA[
let children = this._children;
while (children.lastChild)
children.removeChild(children.lastChild);
let items = this._getRemoteTabs();
children.setItems(items.map(this.createItem));
]]></body>
</method>
<field name="_children">
document.getAnonymousElementByAttribute(this, "anonid", "child-items");
</field>
<field name="scrollBoxObject">this._children.scrollBoxObject</field>
<method name="_getRemoteTabs">
<body><![CDATA[
// Don't do anything if the tabs engine isn't ready
let engine = Weave.Engines.get("tabs");
if (!engine)
return [];
// Don't bother refetching tabs if we already did so recently
let lastFetch = Weave.Svc.Prefs.get("lastTabFetch", 0);
let now = Math.floor(Date.now() / 1000);
if (now - lastFetch >= 30) {
// Force a sync only for the tabs engine
engine.lastModified = null;
engine.sync();
Weave.Svc.Prefs.set("lastTabFetch", now);
};
// Generate the list of tabs
let tabs = [];
for (let [guid, client] in Iterator(engine.getAllClients())) {
tabs.push({ name: client.clientName });
client.tabs.forEach(function({title, urlHistory, icon}) {
let pageURL = urlHistory[0];
// Skip tabs that are already open
if (engine.locallyOpenTabMatchesURL(pageURL))
return;
tabs.push({
title: title || pageURL,
uri: pageURL,
icon: Weave.Utils.getIcon(icon, "chrome://browser/skin/images/tab.png")
});
});
};
return tabs;
]]></body>
</method>
<method name="createItem">
<parameter name="aItem"/>
<body>
<![CDATA[
const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let child = document.createElementNS(XULNS, "autocompleteresult");
child.setAttribute("value", aItem.title || aItem.name);
if (aItem.name) {
child.setAttribute("class", "remotetabs-item-title");
} else {
child.setAttribute("class", "remotetabs-item");
child.setAttribute("url", aItem.uri);
child.setAttribute("src", aItem.icon);
}
return child;
]]>
</body>
</method>
</implementation>
</binding>
<binding id="richlistbox-batch" extends="chrome://global/content/bindings/richlistbox.xml#richlistbox">
<handlers>
<handler event="scroll">
<![CDATA[
// if there no more items to insert, just return early
if (this._items.length == 0)
return;
if (this._contentScrollHeight == -1) {
let scrollheight = {};
this.scrollBoxObject.getScrolledSize({}, scrollheight);
this._contentScrollHeight = scrollheight.value;
}
let y = {};
this.scrollBoxObject.getPosition({}, y);
let scrollRatio = (y.value + this._childrenHeight) / this._contentScrollHeight;
// If we're scrolled 80% to the bottom of the list, append the next
// set of items
if (scrollRatio > 0.8)
this._insertItems();
]]>
</handler>
</handlers>
<implementation>
<!-- Number of elements to add to the list initially. If there are more
than this many elements to display, only add them to the list once
the user has scrolled towards them. This is a performance
optimization to avoid locking up while attempting to append hundreds
of nodes to our richlistbox.
-->
<property name="batchSize" readonly="true" onget="return this.getAttribute('batch')"/>
<field name="_childrenHeight">this.scrollBoxObject.height;</field>
<field name="_items">[]</field>
<method name="setItems">
<parameter name="aItems"/>
<body><![CDATA[
this._items = aItems;
this._insertItems();
]]></body>
</method>
<method name="_insertItems">
<body><![CDATA[
let items = this._items.splice(0, this.batchSize);
if (!items.length)
return; // no items to insert
let count = items.length;
for (let i = 0; i<count; i++)
this.appendChild(items[i]);
// make sure we recalculate the scrollHeight of the content
this._contentScrollHeight = -1;
]]></body>
</method>
</implementation>
</binding>
<binding id="richlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<handlers>
<handler event="mousedown" phase="capturing">
<![CDATA[
event.stopPropagation();
]]>
</handler>
</handlers>
</binding>
<binding id="content-navigator">
<content pack="end">
<children includes="vbox"/>
<xul:hbox class="content-navigator-box panel-dark" pack="end">
<children includes="textbox|arrowscrollbox"/>
<xul:toolbarbutton anonid="previous-button" class="content-navigator-item button-image previous-button" xbl:inherits="command=previous"/>
<xul:toolbarbutton anonid="next-button" class="content-navigator-item button-image next-button" xbl:inherits="command=next"/>
</xul:hbox>
</content>
<implementation>
<field name="_previousButton">
document.getAnonymousElementByAttribute(this, "anonid", "previous-button");
</field>
<field name="_nextButton">
document.getAnonymousElementByAttribute(this, "anonid", "next-button");
</field>
<field name="_spacer">
document.getElementById(this.getAttribute("spacer"));
</field>
<method name="contentHasChanged">
<body><![CDATA[
let height = Math.floor(this.getBoundingClientRect().height);
this.top = window.innerHeight - height;
this._spacer.setAttribute("height", height);
]]></body>
</method>
<property name="isActive" onget="return !this._spacer.hidden;"/>
<field name="_model">null</field>
<method name="show">
<parameter name="aModel" />
<body><![CDATA[
// call the hide callback of the current object if any
if (this._model && this._model.type != aModel.type)
this._model.hide();
this.setAttribute("type", aModel.type);
this.setAttribute("next", aModel.commands.next);
this.setAttribute("previous", aModel.commands.previous);
this.setAttribute("close", aModel.commands.close);
// buttons attributes sync with commands doesn not look updated when
// we dynamically switch the "command" attribute so we need to ensure
// the disabled state of the buttons is right when switching commands
this._previousButton.disabled = document.getElementById(aModel.commands.previous).disabled;
this._nextButton.disabled = document.getElementById(aModel.commands.next).disabled;
this._model = aModel;
this.contentHasChanged();
this._spacer.hidden = false;
]]></body>
</method>
<method name="hide">
<parameter name="aModel" />
<body><![CDATA[
this.removeAttribute("next");
this.removeAttribute("previous");
this.removeAttribute("close");
this.removeAttribute("type");
this._model = null;
this._spacer.hidden = true;
]]></body>
</method>
</implementation>
</binding>
<binding id="menulist" display="xul:box" extends="chrome://global/content/bindings/menulist.xml#menulist">
<handlers>
<handler event="mousedown" phase="capturing">
<![CDATA[
// Stop the normal menupopup from appearing
event.stopPropagation();
]]>
</handler>
<handler event="click" button="0">
<![CDATA[
if (this.disabled || this.itemCount == 0)
return;
this.focus();
MenuListHelperUI.show(this);
]]>
</handler>
</handlers>
</binding>
<binding id="chrome-select-option">
<content orient="horizontal" flex="1">
<xul:image class="chrome-select-option-image" anonid="check"/>
<xul:label anonid="label" xbl:inherits="value=label"/>
</content>
<implementation>
<property name="selected">
<getter>
<![CDATA[
return this.hasAttribute("selected");
]]>
</getter>
<setter>
<![CDATA[
if (val)
this.setAttribute("selected", "true");
else
this.removeAttribute("selected");
return val;
]]>
</setter>
</property>
</implementation>
</binding>
<binding id="select-button" extends="xul:box">
<content>
<svg:svg width="11px" version="1.1" xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: -moz-calc(50% - 2px); left: 4px;">
<svg:polyline points="1 1 5 6 9 1" stroke="#414141" stroke-width="2" stroke-linecap="round" fill="transparent" stroke-linejoin="round"/>
</svg:svg>
</content>
</binding>
</bindings>