summaryrefslogtreecommitdiffstats
path: root/components/places
diff options
context:
space:
mode:
authorThomas Groman <tgroman@nuegia.net>2020-04-20 20:49:37 -0700
committerThomas Groman <tgroman@nuegia.net>2020-04-20 20:49:37 -0700
commitf9cab004186edb425a9b88ad649726605080a17c (patch)
treee2dae51d3144e83d097a12e7a1499e3ea93f90be /components/places
parentf428692de8b59ab89a66502c079e1823dfda8aeb (diff)
downloadwebbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar
webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.gz
webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.lz
webbrowser-f9cab004186edb425a9b88ad649726605080a17c.tar.xz
webbrowser-f9cab004186edb425a9b88ad649726605080a17c.zip
move browser to webbrowser/
Diffstat (limited to 'components/places')
-rw-r--r--components/places/PlacesUIUtils.jsm1373
-rw-r--r--components/places/content/bookmarkProperties.js675
-rw-r--r--components/places/content/bookmarkProperties.xul43
-rw-r--r--components/places/content/bookmarksPanel.js25
-rw-r--r--components/places/content/bookmarksPanel.xul55
-rw-r--r--components/places/content/browserPlacesViews.js1754
-rw-r--r--components/places/content/controller.js1895
-rw-r--r--components/places/content/downloadsViewOverlay.xul47
-rw-r--r--components/places/content/editBookmarkOverlay.js1063
-rw-r--r--components/places/content/editBookmarkOverlay.xul228
-rw-r--r--components/places/content/history-panel.js91
-rw-r--r--components/places/content/history-panel.xul95
-rw-r--r--components/places/content/menu.xml488
-rw-r--r--components/places/content/moveBookmarks.js54
-rw-r--r--components/places/content/moveBookmarks.xul53
-rw-r--r--components/places/content/organizer.css7
-rw-r--r--components/places/content/places.css16
-rw-r--r--components/places/content/places.js1553
-rw-r--r--components/places/content/places.xul471
-rw-r--r--components/places/content/placesOverlay.xul247
-rw-r--r--components/places/content/sidebarUtils.js108
-rw-r--r--components/places/content/tree.xml789
-rw-r--r--components/places/content/treeView.js1770
-rw-r--r--components/places/jar.mn34
-rw-r--r--components/places/moz.build9
25 files changed, 0 insertions, 12943 deletions
diff --git a/components/places/PlacesUIUtils.jsm b/components/places/PlacesUIUtils.jsm
deleted file mode 100644
index e3a9e13..0000000
--- a/components/places/PlacesUIUtils.jsm
+++ /dev/null
@@ -1,1373 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-this.EXPORTED_SYMBOLS = ["PlacesUIUtils"];
-
-var Ci = Components.interfaces;
-var Cc = Components.classes;
-var Cr = Components.results;
-var Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
- "resource://gre/modules/PluralForm.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
- "resource://gre/modules/PrivateBrowsingUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
- "resource:///modules/RecentWindow.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
- Cu.import("resource://gre/modules/PlacesUtils.jsm");
- return PlacesUtils;
-});
-
-this.PlacesUIUtils = {
- ORGANIZER_LEFTPANE_VERSION: 7,
- ORGANIZER_FOLDER_ANNO: "PlacesOrganizer/OrganizerFolder",
- ORGANIZER_QUERY_ANNO: "PlacesOrganizer/OrganizerQuery",
-
- LOAD_IN_SIDEBAR_ANNO: "bookmarkProperties/loadInSidebar",
- DESCRIPTION_ANNO: "bookmarkProperties/description",
-
- TYPE_TAB_DROP: "application/x-moz-tabbrowser-tab",
-
- /**
- * Makes a URI from a spec, and do fixup
- * @param aSpec
- * The string spec of the URI
- * @returns A URI object for the spec.
- */
- createFixedURI: function PUIU_createFixedURI(aSpec) {
- return URIFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
- },
-
- getFormattedString: function PUIU_getFormattedString(key, params) {
- return bundle.formatStringFromName(key, params, params.length);
- },
-
- /**
- * Get a localized plural string for the specified key name and numeric value
- * substituting parameters.
- *
- * @param aKey
- * String, key for looking up the localized string in the bundle
- * @param aNumber
- * Number based on which the final localized form is looked up
- * @param aParams
- * Array whose items will substitute #1, #2,... #n parameters
- * in the string.
- *
- * @see https://developer.mozilla.org/en/Localization_and_Plurals
- * @return The localized plural string.
- */
- getPluralString: function PUIU_getPluralString(aKey, aNumber, aParams) {
- let str = PluralForm.get(aNumber, bundle.GetStringFromName(aKey));
-
- // Replace #1 with aParams[0], #2 with aParams[1], and so on.
- return str.replace(/\#(\d+)/g, function (matchedId, matchedNumber) {
- let param = aParams[parseInt(matchedNumber, 10) - 1];
- return param !== undefined ? param : matchedId;
- });
- },
-
- getString: function PUIU_getString(key) {
- return bundle.GetStringFromName(key);
- },
-
- get _copyableAnnotations() [
- this.DESCRIPTION_ANNO,
- this.LOAD_IN_SIDEBAR_ANNO,
- PlacesUtils.POST_DATA_ANNO,
- PlacesUtils.READ_ONLY_ANNO,
- ],
-
- /**
- * Get a transaction for copying a uri item (either a bookmark or a history
- * entry) from one container to another.
- *
- * @param aData
- * JSON object of dropped or pasted item properties
- * @param aContainer
- * The container being copied into
- * @param aIndex
- * The index within the container the item is copied to
- * @return A nsITransaction object that performs the copy.
- *
- * @note Since a copy creates a completely new item, only some internal
- * annotations are synced from the old one.
- * @see this._copyableAnnotations for the list of copyable annotations.
- */
- _getURIItemCopyTransaction:
- function PUIU__getURIItemCopyTransaction(aData, aContainer, aIndex)
- {
- let transactions = [];
- if (aData.dateAdded) {
- transactions.push(
- new PlacesEditItemDateAddedTransaction(null, aData.dateAdded)
- );
- }
- if (aData.lastModified) {
- transactions.push(
- new PlacesEditItemLastModifiedTransaction(null, aData.lastModified)
- );
- }
-
- let keyword = aData.keyword || null;
- let annos = [];
- if (aData.annos) {
- annos = aData.annos.filter(function (aAnno) {
- return this._copyableAnnotations.indexOf(aAnno.name) != -1;
- }, this);
- }
-
- return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(aData.uri),
- aContainer, aIndex, aData.title,
- keyword, annos, transactions);
- },
-
- /**
- * Gets a transaction for copying (recursively nesting to include children)
- * a folder (or container) and its contents from one folder to another.
- *
- * @param aData
- * Unwrapped dropped folder data - Obj containing folder and children
- * @param aContainer
- * The container we are copying into
- * @param aIndex
- * The index in the destination container to insert the new items
- * @return A nsITransaction object that will perform the copy.
- *
- * @note Since a copy creates a completely new item, only some internal
- * annotations are synced from the old one.
- * @see this._copyableAnnotations for the list of copyable annotations.
- */
- _getFolderCopyTransaction(aData, aContainer, aIndex) {
- function getChildItemsTransactions(aRoot) {
- let transactions = [];
- let index = aIndex;
- for (let i = 0; i < aRoot.childCount; ++i) {
- let child = aRoot.getChild(i);
- // Temporary hacks until we switch to PlacesTransactions.jsm.
- let isLivemark =
- PlacesUtils.annotations.itemHasAnnotation(child.itemId,
- PlacesUtils.LMANNO_FEEDURI);
- let [node] = PlacesUtils.unwrapNodes(
- PlacesUtils.wrapNode(child, PlacesUtils.TYPE_X_MOZ_PLACE, isLivemark),
- PlacesUtils.TYPE_X_MOZ_PLACE
- );
-
- // Make sure that items are given the correct index, this will be
- // passed by the transaction manager to the backend for the insertion.
- // Insertion behaves differently for DEFAULT_INDEX (append).
- if (aIndex != PlacesUtils.bookmarks.DEFAULT_INDEX) {
- index = i;
- }
-
- if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) {
- if (node.livemark && node.annos) {
- transactions.push(
- PlacesUIUtils._getLivemarkCopyTransaction(node, aContainer, index)
- );
- }
- else {
- transactions.push(
- PlacesUIUtils._getFolderCopyTransaction(node, aContainer, index)
- );
- }
- }
- else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR) {
- transactions.push(new PlacesCreateSeparatorTransaction(-1, index));
- }
- else if (node.type == PlacesUtils.TYPE_X_MOZ_PLACE) {
- transactions.push(
- PlacesUIUtils._getURIItemCopyTransaction(node, -1, index)
- );
- }
- else {
- throw new Error("Unexpected item under a bookmarks folder");
- }
- }
- return transactions;
- }
-
- if (aContainer == PlacesUtils.tagsFolderId) { // Copying into a tag folder.
- let transactions = [];
- if (!aData.livemark && aData.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER) {
- let {root} = PlacesUtils.getFolderContents(aData.id, false, false);
- let urls = PlacesUtils.getURLsForContainerNode(root);
- root.containerOpen = false;
- for (let { uri } of urls) {
- transactions.push(
- new PlacesTagURITransaction(NetUtil.newURI(uri), [aData.title])
- );
- }
- }
- return new PlacesAggregatedTransaction("addTags", transactions);
- }
-
- if (aData.livemark && aData.annos) { // Copying a livemark.
- return this._getLivemarkCopyTransaction(aData, aContainer, aIndex);
- }
-
- let {root} = PlacesUtils.getFolderContents(aData.id, false, false);
- let transactions = getChildItemsTransactions(root);
- root.containerOpen = false;
-
- if (aData.dateAdded) {
- transactions.push(
- new PlacesEditItemDateAddedTransaction(null, aData.dateAdded)
- );
- }
- if (aData.lastModified) {
- transactions.push(
- new PlacesEditItemLastModifiedTransaction(null, aData.lastModified)
- );
- }
-
- let annos = [];
- if (aData.annos) {
- annos = aData.annos.filter(function (aAnno) {
- return this._copyableAnnotations.indexOf(aAnno.name) != -1;
- }, this);
- }
-
- return new PlacesCreateFolderTransaction(aData.title, aContainer, aIndex,
- annos, transactions);
- },
-
- /**
- * Gets a transaction for copying a live bookmark item from one container to
- * another.
- *
- * @param aData
- * Unwrapped live bookmarkmark data
- * @param aContainer
- * The container we are copying into
- * @param aIndex
- * The index in the destination container to insert the new items
- * @return A nsITransaction object that will perform the copy.
- *
- * @note Since a copy creates a completely new item, only some internal
- * annotations are synced from the old one.
- * @see this._copyableAnnotations for the list of copyable annotations.
- */
- _getLivemarkCopyTransaction:
- function PUIU__getLivemarkCopyTransaction(aData, aContainer, aIndex)
- {
- if (!aData.livemark || !aData.annos) {
- throw new Error("node is not a livemark");
- }
-
- let feedURI, siteURI;
- let annos = [];
- if (aData.annos) {
- annos = aData.annos.filter(function (aAnno) {
- if (aAnno.name == PlacesUtils.LMANNO_FEEDURI) {
- feedURI = PlacesUtils._uri(aAnno.value);
- }
- else if (aAnno.name == PlacesUtils.LMANNO_SITEURI) {
- siteURI = PlacesUtils._uri(aAnno.value);
- }
- return this._copyableAnnotations.indexOf(aAnno.name) != -1
- }, this);
- }
-
- return new PlacesCreateLivemarkTransaction(feedURI, siteURI, aData.title,
- aContainer, aIndex, annos);
- },
-
- /**
- * Test if a bookmark item = a live bookmark item.
- *
- * @param aItemId
- * item identifier
- * @return true if a live bookmark item, false otherwise.
- *
- * @note Maybe this should be removed later, see bug 1072833.
- */
- _isLivemark:
- function PUIU__isLivemark(aItemId)
- {
- // Since this check may be done on each dragover event, it's worth maintaining
- // a cache.
- let self = PUIU__isLivemark;
- if (!("ids" in self)) {
- const LIVEMARK_ANNO = PlacesUtils.LMANNO_FEEDURI;
-
- let idsVec = PlacesUtils.annotations.getItemsWithAnnotation(LIVEMARK_ANNO);
- self.ids = new Set(idsVec);
-
- let obs = Object.freeze({
- QueryInterface: XPCOMUtils.generateQI(Ci.nsIAnnotationObserver),
-
- onItemAnnotationSet(itemId, annoName) {
- if (annoName == LIVEMARK_ANNO)
- self.ids.add(itemId);
- },
-
- onItemAnnotationRemoved(itemId, annoName) {
- // If annoName is set to an empty string, the item is gone.
- if (annoName == LIVEMARK_ANNO || annoName == "")
- self.ids.delete(itemId);
- },
-
- onPageAnnotationSet() { },
- onPageAnnotationRemoved() { },
- });
- PlacesUtils.annotations.addObserver(obs);
- PlacesUtils.registerShutdownFunction(() => {
- PlacesUtils.annotations.removeObserver(obs);
- });
- }
- return self.ids.has(aItemId);
- },
-
- /**
- * Constructs a Transaction for the drop or paste of a blob of data into
- * a container.
- * @param data
- * The unwrapped data blob of dropped or pasted data.
- * @param type
- * The content type of the data
- * @param container
- * The container the data was dropped or pasted into
- * @param index
- * The index within the container the item was dropped or pasted at
- * @param copy
- * The drag action was copy, so don't move folders or links.
- * @returns An object implementing nsITransaction that can perform
- * the move/insert.
- */
- makeTransaction:
- function PUIU_makeTransaction(data, type, container, index, copy)
- {
- switch (data.type) {
- case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER:
- if (copy) {
- return this._getFolderCopyTransaction(data, container, index);
- }
-
- // Otherwise move the item.
- return new PlacesMoveItemTransaction(data.id, container, index);
- break;
- case PlacesUtils.TYPE_X_MOZ_PLACE:
- if (copy || data.id == -1) { // Id is -1 if the place is not bookmarked.
- return this._getURIItemCopyTransaction(data, container, index);
- }
-
- // Otherwise move the item.
- return new PlacesMoveItemTransaction(data.id, container, index);
- break;
- case PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR:
- if (copy) {
- // There is no data in a separator, so copying it just amounts to
- // inserting a new separator.
- return new PlacesCreateSeparatorTransaction(container, index);
- }
-
- // Otherwise move the item.
- return new PlacesMoveItemTransaction(data.id, container, index);
- break;
- default:
- if (type == PlacesUtils.TYPE_X_MOZ_URL ||
- type == PlacesUtils.TYPE_UNICODE ||
- type == this.TYPE_TAB_DROP) {
- let title = type != PlacesUtils.TYPE_UNICODE ? data.title
- : data.uri;
- return new PlacesCreateBookmarkTransaction(PlacesUtils._uri(data.uri),
- container, index, title);
- }
- }
- return null;
- },
-
- /**
- * Shows the bookmark dialog corresponding to the specified info.
- *
- * @param aInfo
- * Describes the item to be edited/added in the dialog.
- * See documentation at the top of bookmarkProperties.js
- * @param aWindow
- * Owner window for the new dialog.
- *
- * @see documentation at the top of bookmarkProperties.js
- * @return true if any transaction has been performed, false otherwise.
- */
- showBookmarkDialog:
- function PUIU_showBookmarkDialog(aInfo, aParentWindow) {
- // Preserve size attributes differently based on the fact the dialog has
- // a folder picker or not, since it needs more horizontal space than the
- // other controls.
- let hasFolderPicker = !("hiddenRows" in aInfo) ||
- aInfo.hiddenRows.indexOf("folderPicker") == -1;
- // Use a different chrome url to persist different sizes.
- let dialogURL = hasFolderPicker ?
- "chrome://browser/content/places/bookmarkProperties2.xul" :
- "chrome://browser/content/places/bookmarkProperties.xul";
-
- let features = "centerscreen,chrome,modal,resizable=yes";
- aParentWindow.openDialog(dialogURL, "", features, aInfo);
- return ("performed" in aInfo && aInfo.performed);
- },
-
- _getTopBrowserWin: function PUIU__getTopBrowserWin() {
- return RecentWindow.getMostRecentBrowserWindow();
- },
-
- /**
- * Returns the closet ancestor places view for the given DOM node
- * @param aNode
- * a DOM node
- * @return the closet ancestor places view if exists, null otherwsie.
- */
- getViewForNode: function PUIU_getViewForNode(aNode) {
- let node = aNode;
-
- // The view for a <menu> of which its associated menupopup is a places
- // view, is the menupopup.
- if (node.localName == "menu" && !node._placesNode &&
- node.lastChild._placesView)
- return node.lastChild._placesView;
-
- while (node instanceof Ci.nsIDOMElement) {
- if (node._placesView)
- return node._placesView;
- if (node.localName == "tree" && node.getAttribute("type") == "places")
- return node;
-
- node = node.parentNode;
- }
-
- return null;
- },
-
- /**
- * By calling this before visiting an URL, the visit will be associated to a
- * TRANSITION_TYPED transition (if there is no a referrer).
- * This is used when visiting pages from the history menu, history sidebar,
- * url bar, url autocomplete results, and history searches from the places
- * organizer. If this is not called visits will be marked as
- * TRANSITION_LINK.
- */
- markPageAsTyped: function PUIU_markPageAsTyped(aURL) {
- PlacesUtils.history.markPageAsTyped(this.createFixedURI(aURL));
- },
-
- /**
- * By calling this before visiting an URL, the visit will be associated to a
- * TRANSITION_BOOKMARK transition.
- * This is used when visiting pages from the bookmarks menu,
- * personal toolbar, and bookmarks from within the places organizer.
- * If this is not called visits will be marked as TRANSITION_LINK.
- */
- markPageAsFollowedBookmark: function PUIU_markPageAsFollowedBookmark(aURL) {
- PlacesUtils.history.markPageAsFollowedBookmark(this.createFixedURI(aURL));
- },
-
- /**
- * By calling this before visiting an URL, any visit in frames will be
- * associated to a TRANSITION_FRAMED_LINK transition.
- * This is actually used to distinguish user-initiated visits in frames
- * so automatic visits can be correctly ignored.
- */
- markPageAsFollowedLink: function PUIU_markPageAsFollowedLink(aURL) {
- PlacesUtils.history.markPageAsFollowedLink(this.createFixedURI(aURL));
- },
-
- /**
- * Allows opening of javascript/data URI only if the given node is
- * bookmarked (see bug 224521).
- * @param aURINode
- * a URI node
- * @param aWindow
- * a window on which a potential error alert is shown on.
- * @return true if it's safe to open the node in the browser, false otherwise.
- *
- */
- checkURLSecurity: function PUIU_checkURLSecurity(aURINode, aWindow) {
- if (PlacesUtils.nodeIsBookmark(aURINode))
- return true;
-
- var uri = PlacesUtils._uri(aURINode.uri);
- if (uri.schemeIs("javascript") || uri.schemeIs("data")) {
- const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
- var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
- getService(Ci.nsIStringBundleService).
- createBundle(BRANDING_BUNDLE_URI).
- GetStringFromName("brandShortName");
-
- var errorStr = this.getString("load-js-data-url-error");
- Services.prompt.alert(aWindow, brandShortName, errorStr);
- return false;
- }
- return true;
- },
-
- /**
- * Get the description associated with a document, as specified in a <META>
- * element.
- * @param doc
- * A DOM Document to get a description for
- * @returns A description string if a META element was discovered with a
- * "description" or "httpequiv" attribute, empty string otherwise.
- */
- getDescriptionFromDocument: function PUIU_getDescriptionFromDocument(doc) {
- var metaElements = doc.getElementsByTagName("META");
- for (var i = 0; i < metaElements.length; ++i) {
- if (metaElements[i].name.toLowerCase() == "description" ||
- metaElements[i].httpEquiv.toLowerCase() == "description") {
- return metaElements[i].content;
- }
- }
- return "";
- },
-
- /**
- * Retrieve the description of an item
- * @param aItemId
- * item identifier
- * @returns the description of the given item, or an empty string if it is
- * not set.
- */
- getItemDescription: function PUIU_getItemDescription(aItemId) {
- if (PlacesUtils.annotations.itemHasAnnotation(aItemId, this.DESCRIPTION_ANNO))
- return PlacesUtils.annotations.getItemAnnotation(aItemId, this.DESCRIPTION_ANNO);
- return "";
- },
-
- /**
- * Check whether or not the given node represents a removable entry (either in
- * history or in bookmarks).
- *
- * @param aNode
- * a node, except the root node of a query.
- * @return true if the aNode represents a removable entry, false otherwise.
- */
- canUserRemove: function (aNode) {
- let parentNode = aNode.parent;
- if (!parentNode)
- throw new Error("canUserRemove doesn't accept root nodes");
-
- // If it's not a bookmark, we can remove it unless it's a child of a
- // livemark.
- if (aNode.itemId == -1) {
- // Rather than executing a db query, checking the existence of the feedURI
- // annotation, detect livemark children by the fact that they are the only
- // direct non-bookmark children of bookmark folders.
- return !PlacesUtils.nodeIsFolder(parentNode);
- }
-
- // Generally it's always possible to remove children of a query.
- if (PlacesUtils.nodeIsQuery(parentNode))
- return true;
-
- // Otherwise it has to be a child of an editable folder.
- return !this.isContentsReadOnly(parentNode);
- },
-
- /**
- * DO NOT USE THIS API IN ADDONS. IT IS VERY LIKELY TO CHANGE WHEN THE SWITCH
- * TO GUIDS IS COMPLETE (BUG 1071511).
- *
- * Check whether or not the given node or item-id points to a folder which
- * should not be modified by the user (i.e. its children should be unremovable
- * and unmovable, new children should be disallowed, etc).
- * These semantics are not inherited, meaning that read-only folder may
- * contain editable items (for instance, the places root is read-only, but all
- * of its direct children aren't).
- *
- * You should only pass folder item ids or folder nodes for aNodeOrItemId.
- * While this is only enforced for the node case (if an item id of a separator
- * or a bookmark is passed, false is returned), it's considered the caller's
- * job to ensure that it checks a folder.
- * Also note that folder-shortcuts should only be passed as result nodes.
- * Otherwise they are just treated as bookmarks (i.e. false is returned).
- *
- * @param aNodeOrItemId
- * any item id or result node.
- * @throws if aNodeOrItemId is neither an item id nor a folder result node.
- * @note livemark "folders" are considered read-only (but see bug 1072833).
- * @return true if aItemId points to a read-only folder, false otherwise.
- */
- isContentsReadOnly: function (aNodeOrItemId) {
- let itemId;
- if (typeof(aNodeOrItemId) == "number") {
- itemId = aNodeOrItemId;
- }
- else if (PlacesUtils.nodeIsFolder(aNodeOrItemId)) {
- itemId = PlacesUtils.getConcreteItemId(aNodeOrItemId);
- }
- else {
- throw new Error("invalid value for aNodeOrItemId");
- }
-
- if (itemId == PlacesUtils.placesRootId || this._isLivemark(itemId))
- return true;
-
- // leftPaneFolderId, and as a result, allBookmarksFolderId, is a lazy getter
- // performing at least a synchronous DB query (and on its very first call
- // in a fresh profile, it also creates the entire structure).
- // Therefore we don't want to this function, which is called very often by
- // isCommandEnabled, to ever be the one that invokes it first, especially
- // because isCommandEnabled may be called way before the left pane folder is
- // even created (for example, if the user only uses the bookmarks menu or
- // toolbar for managing bookmarks). To do so, we avoid comparing to those
- // special folder if the lazy getter is still in place. This is safe merely
- // because the only way to access the left pane contents goes through
- // "resolving" the leftPaneFolderId getter.
- if ("get" in Object.getOwnPropertyDescriptor(this, "leftPaneFolderId"))
- return false;
-
- return itemId == this.leftPaneFolderId ||
- itemId == this.allBookmarksFolderId;
- },
-
- /**
- * Gives the user a chance to cancel loading lots of tabs at once
- */
- _confirmOpenInTabs:
- function PUIU__confirmOpenInTabs(numTabsToOpen, aWindow) {
- const WARN_ON_OPEN_PREF = "browser.tabs.warnOnOpen";
- var reallyOpen = true;
-
- if (Services.prefs.getBoolPref(WARN_ON_OPEN_PREF)) {
- if (numTabsToOpen >= Services.prefs.getIntPref("browser.tabs.maxOpenBeforeWarn")) {
- // default to true: if it were false, we wouldn't get this far
- var warnOnOpen = { value: true };
-
- var messageKey = "tabs.openWarningMultipleBranded";
- var openKey = "tabs.openButtonMultiple";
- const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties";
- var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"].
- getService(Ci.nsIStringBundleService).
- createBundle(BRANDING_BUNDLE_URI).
- GetStringFromName("brandShortName");
-
- var buttonPressed = Services.prompt.confirmEx(
- aWindow,
- this.getString("tabs.openWarningTitle"),
- this.getFormattedString(messageKey, [numTabsToOpen, brandShortName]),
- (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
- (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1),
- this.getString(openKey), null, null,
- this.getFormattedString("tabs.openWarningPromptMeBranded",
- [brandShortName]),
- warnOnOpen
- );
-
- reallyOpen = (buttonPressed == 0);
- // don't set the pref unless they press OK and it's false
- if (reallyOpen && !warnOnOpen.value)
- Services.prefs.setBoolPref(WARN_ON_OPEN_PREF, false);
- }
- }
-
- return reallyOpen;
- },
-
- /** aItemsToOpen needs to be an array of objects of the form:
- * {uri: string, isBookmark: boolean}
- */
- _openTabset: function PUIU__openTabset(aItemsToOpen, aEvent, aWindow) {
- if (!aItemsToOpen.length)
- return;
-
- // Prefer the caller window if it's a browser window, otherwise use
- // the top browser window.
- var browserWindow = null;
- browserWindow =
- aWindow && aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser" ?
- aWindow : this._getTopBrowserWin();
-
- var urls = [];
- let skipMarking = browserWindow && PrivateBrowsingUtils.isWindowPrivate(browserWindow);
- for (let item of aItemsToOpen) {
- urls.push(item.uri);
- if (skipMarking) {
- continue;
- }
-
- if (item.isBookmark)
- this.markPageAsFollowedBookmark(item.uri);
- else
- this.markPageAsTyped(item.uri);
- }
-
- // whereToOpenLink doesn't return "window" when there's no browser window
- // open (Bug 630255).
- var where = browserWindow ?
- browserWindow.whereToOpenLink(aEvent, false, true) : "window";
- if (where == "window") {
- // There is no browser window open, thus open a new one.
- var uriList = PlacesUtils.toISupportsString(urls.join("|"));
- var args = Cc["@mozilla.org/supports-array;1"].
- createInstance(Ci.nsISupportsArray);
- args.AppendElement(uriList);
- browserWindow = Services.ww.openWindow(aWindow,
- "chrome://browser/content/browser.xul",
- null, "chrome,dialog=no,all", args);
- return;
- }
-
- var loadInBackground = where == "tabshifted" ? true : false;
- // For consistency, we want all the bookmarks to open in new tabs, instead
- // of having one of them replace the currently focused tab. Hence we call
- // loadTabs with aReplace set to false.
- browserWindow.gBrowser.loadTabs(urls, loadInBackground, false);
- },
-
- openLiveMarkNodesInTabs:
- function PUIU_openLiveMarkNodesInTabs(aNode, aEvent, aView) {
- let window = aView.ownerWindow;
-
- PlacesUtils.livemarks.getLivemark({id: aNode.itemId})
- .then(aLivemark => {
- urlsToOpen = [];
-
- let nodes = aLivemark.getNodesForContainer(aNode);
- for (let node of nodes) {
- urlsToOpen.push({uri: node.uri, isBookmark: false});
- }
-
- if (this._confirmOpenInTabs(urlsToOpen.length, window)) {
- this._openTabset(urlsToOpen, aEvent, window);
- }
- }, Cu.reportError);
- },
-
- openContainerNodeInTabs:
- function PUIU_openContainerInTabs(aNode, aEvent, aView) {
- let window = aView.ownerWindow;
-
- let urlsToOpen = PlacesUtils.getURLsForContainerNode(aNode);
- if (this._confirmOpenInTabs(urlsToOpen.length, window)) {
- this._openTabset(urlsToOpen, aEvent, window);
- }
- },
-
- openURINodesInTabs: function PUIU_openURINodesInTabs(aNodes, aEvent, aView) {
- let window = aView.ownerWindow;
-
- let urlsToOpen = [];
- for (var i=0; i < aNodes.length; i++) {
- // Skip over separators and folders.
- if (PlacesUtils.nodeIsURI(aNodes[i]))
- urlsToOpen.push({uri: aNodes[i].uri, isBookmark: PlacesUtils.nodeIsBookmark(aNodes[i])});
- }
- this._openTabset(urlsToOpen, aEvent, window);
- },
-
- /**
- * Loads the node's URL in the appropriate tab or window or as a web
- * panel given the user's preference specified by modifier keys tracked by a
- * DOM mouse/key event.
- * @param aNode
- * An uri result node.
- * @param aEvent
- * The DOM mouse/key event with modifier keys set that track the
- * user's preferred destination window or tab.
- * @param aView
- * The controller associated with aNode.
- */
- openNodeWithEvent:
- function PUIU_openNodeWithEvent(aNode, aEvent, aView) {
- let window = aView.ownerWindow;
- this._openNodeIn(aNode, window.whereToOpenLink(aEvent, false, true), window);
- },
-
- /**
- * Loads the node's URL in the appropriate tab or window or as a
- * web panel.
- * see also openUILinkIn
- */
- openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView, aPrivate) {
- let window = aView.ownerWindow;
- this._openNodeIn(aNode, aWhere, window, aPrivate);
- },
-
- _openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow, aPrivate=false) {
- if (aNode && PlacesUtils.nodeIsURI(aNode) &&
- this.checkURLSecurity(aNode, aWindow)) {
- let isBookmark = PlacesUtils.nodeIsBookmark(aNode);
-
- if (!PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
- if (isBookmark)
- this.markPageAsFollowedBookmark(aNode.uri);
- else
- this.markPageAsTyped(aNode.uri);
- }
-
- // Check whether the node is a bookmark which should be opened as
- // a web panel
- if (aWhere == "current" && isBookmark) {
- if (PlacesUtils.annotations
- .itemHasAnnotation(aNode.itemId, this.LOAD_IN_SIDEBAR_ANNO)) {
- let browserWin = this._getTopBrowserWin();
- if (browserWin) {
- browserWin.openWebPanel(aNode.title, aNode.uri);
- return;
- }
- }
- }
- aWindow.openUILinkIn(aNode.uri, aWhere, {
- inBackground: Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground"),
- private: aPrivate,
- });
- }
- },
-
- /**
- * Helper for guessing scheme from an url string.
- * Used to avoid nsIURI overhead in frequently called UI functions.
- *
- * @param aUrlString the url to guess the scheme from.
- *
- * @return guessed scheme for this url string.
- *
- * @note this is not supposed be perfect, so use it only for UI purposes.
- */
- guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) {
- return aUrlString.substr(0, aUrlString.indexOf(":"));
- },
-
- getBestTitle: function PUIU_getBestTitle(aNode, aDoNotCutTitle) {
- var title;
- if (!aNode.title && PlacesUtils.nodeIsURI(aNode)) {
- // if node title is empty, try to set the label using host and filename
- // PlacesUtils._uri() will throw if aNode.uri is not a valid URI
- try {
- var uri = PlacesUtils._uri(aNode.uri);
- var host = uri.host;
- var fileName = uri.QueryInterface(Ci.nsIURL).fileName;
- // if fileName is empty, use path to distinguish labels
- if (aDoNotCutTitle) {
- title = host + uri.path;
- } else {
- title = host + (fileName ?
- (host ? "/" + this.ellipsis + "/" : "") + fileName :
- uri.path);
- }
- }
- catch (e) {
- // Use (no title) for non-standard URIs (data:, javascript:, ...)
- title = "";
- }
- }
- else
- title = aNode.title;
-
- return title || this.getString("noTitle");
- },
-
- get leftPaneQueries() {
- // build the map
- this.leftPaneFolderId;
- return this.leftPaneQueries;
- },
-
- // Get the folder id for the organizer left-pane folder.
- get leftPaneFolderId() {
- let leftPaneRoot = -1;
- let allBookmarksId;
-
- // Shortcuts to services.
- let bs = PlacesUtils.bookmarks;
- let as = PlacesUtils.annotations;
-
- // This is the list of the left pane queries.
- let queries = {
- "PlacesRoot": { title: "" },
- "History": { title: this.getString("OrganizerQueryHistory") },
- "Downloads": { title: this.getString("OrganizerQueryDownloads") },
- "Tags": { title: this.getString("OrganizerQueryTags") },
- "AllBookmarks": { title: this.getString("OrganizerQueryAllBookmarks") },
- "BookmarksToolbar":
- { title: null,
- concreteTitle: PlacesUtils.getString("BookmarksToolbarFolderTitle"),
- concreteId: PlacesUtils.toolbarFolderId },
- "BookmarksMenu":
- { title: null,
- concreteTitle: PlacesUtils.getString("BookmarksMenuFolderTitle"),
- concreteId: PlacesUtils.bookmarksMenuFolderId },
- "UnfiledBookmarks":
- { title: null,
- concreteTitle: PlacesUtils.getString("UnsortedBookmarksFolderTitle"),
- concreteId: PlacesUtils.unfiledBookmarksFolderId },
- };
- // All queries but PlacesRoot.
- const EXPECTED_QUERY_COUNT = 7;
-
- // Removes an item and associated annotations, ignoring eventual errors.
- function safeRemoveItem(aItemId) {
- try {
- if (as.itemHasAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) &&
- !(as.getItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO) in queries)) {
- // Some extension annotated their roots with our query annotation,
- // so we should not delete them.
- return;
- }
- // removeItemAnnotation does not check if item exists, nor the anno,
- // so this is safe to do.
- as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO);
- as.removeItemAnnotation(aItemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO);
- // This will throw if the annotation is an orphan.
- bs.removeItem(aItemId);
- }
- catch(e) { /* orphan anno */ }
- }
-
- // Returns true if item really exists, false otherwise.
- function itemExists(aItemId) {
- try {
- bs.getItemIndex(aItemId);
- return true;
- }
- catch(e) {
- return false;
- }
- }
-
- // Get all items marked as being the left pane folder.
- let items = as.getItemsWithAnnotation(this.ORGANIZER_FOLDER_ANNO);
- if (items.length > 1) {
- // Something went wrong, we cannot have more than one left pane folder,
- // remove all left pane folders and continue. We will create a new one.
- items.forEach(safeRemoveItem);
- }
- else if (items.length == 1 && items[0] != -1) {
- leftPaneRoot = items[0];
- // Check that organizer left pane root is valid.
- let version = as.getItemAnnotation(leftPaneRoot, this.ORGANIZER_FOLDER_ANNO);
- if (version != this.ORGANIZER_LEFTPANE_VERSION ||
- !itemExists(leftPaneRoot)) {
- // Invalid root, we must rebuild the left pane.
- safeRemoveItem(leftPaneRoot);
- leftPaneRoot = -1;
- }
- }
-
- if (leftPaneRoot != -1) {
- // A valid left pane folder has been found.
- // Build the leftPaneQueries Map. This is used to quickly access them,
- // associating a mnemonic name to the real item ids.
- delete this.leftPaneQueries;
- this.leftPaneQueries = {};
-
- let items = as.getItemsWithAnnotation(this.ORGANIZER_QUERY_ANNO);
- // While looping through queries we will also check for their validity.
- let queriesCount = 0;
- for (let i = 0; i < items.length; i++) {
- let queryName = as.getItemAnnotation(items[i], this.ORGANIZER_QUERY_ANNO);
-
- // Some extension did use our annotation to decorate their items
- // with icons, so we should check only our elements, to avoid dataloss.
- if (!(queryName in queries))
- continue;
-
- let query = queries[queryName];
- query.itemId = items[i];
-
- if (!itemExists(query.itemId)) {
- // Orphan annotation, bail out and create a new left pane root.
- break;
- }
-
- // Check that all queries have valid parents.
- let parentId = bs.getFolderIdForItem(query.itemId);
- if (items.indexOf(parentId) == -1 && parentId != leftPaneRoot) {
- // The parent is not part of the left pane, bail out and create a new
- // left pane root.
- break;
- }
-
- // Titles could have been corrupted or the user could have changed his
- // locale. Check title and eventually fix it.
- if (bs.getItemTitle(query.itemId) != query.title)
- bs.setItemTitle(query.itemId, query.title);
- if ("concreteId" in query) {
- if (bs.getItemTitle(query.concreteId) != query.concreteTitle)
- bs.setItemTitle(query.concreteId, query.concreteTitle);
- }
-
- // Add the query to our cache.
- this.leftPaneQueries[queryName] = query.itemId;
- queriesCount++;
- }
-
- if (queriesCount != EXPECTED_QUERY_COUNT) {
- // Queries number is wrong, so the left pane must be corrupt.
- // Note: we can't just remove the leftPaneRoot, because some query could
- // have a bad parent, so we have to remove all items one by one.
- items.forEach(safeRemoveItem);
- safeRemoveItem(leftPaneRoot);
- }
- else {
- // Everything is fine, return the current left pane folder.
- delete this.leftPaneFolderId;
- return this.leftPaneFolderId = leftPaneRoot;
- }
- }
-
- // Create a new left pane folder.
- var callback = {
- // Helper to create an organizer special query.
- create_query: function CB_create_query(aQueryName, aParentId, aQueryUrl) {
- let itemId = bs.insertBookmark(aParentId,
- PlacesUtils._uri(aQueryUrl),
- bs.DEFAULT_INDEX,
- queries[aQueryName].title);
- // Mark as special organizer query.
- as.setItemAnnotation(itemId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aQueryName,
- 0, as.EXPIRE_NEVER);
- // We should never backup this, since it changes between profiles.
- as.setItemAnnotation(itemId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
- 0, as.EXPIRE_NEVER);
- // Add to the queries map.
- PlacesUIUtils.leftPaneQueries[aQueryName] = itemId;
- return itemId;
- },
-
- // Helper to create an organizer special folder.
- create_folder: function CB_create_folder(aFolderName, aParentId, aIsRoot) {
- // Left Pane Root Folder.
- let folderId = bs.createFolder(aParentId,
- queries[aFolderName].title,
- bs.DEFAULT_INDEX);
- // We should never backup this, since it changes between profiles.
- as.setItemAnnotation(folderId, PlacesUtils.EXCLUDE_FROM_BACKUP_ANNO, 1,
- 0, as.EXPIRE_NEVER);
-
- if (aIsRoot) {
- // Mark as special left pane root.
- as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_FOLDER_ANNO,
- PlacesUIUtils.ORGANIZER_LEFTPANE_VERSION,
- 0, as.EXPIRE_NEVER);
- }
- else {
- // Mark as special organizer folder.
- as.setItemAnnotation(folderId, PlacesUIUtils.ORGANIZER_QUERY_ANNO, aFolderName,
- 0, as.EXPIRE_NEVER);
- PlacesUIUtils.leftPaneQueries[aFolderName] = folderId;
- }
- return folderId;
- },
-
- runBatched: function CB_runBatched(aUserData) {
- delete PlacesUIUtils.leftPaneQueries;
- PlacesUIUtils.leftPaneQueries = { };
-
- // Left Pane Root Folder.
- leftPaneRoot = this.create_folder("PlacesRoot", bs.placesRoot, true);
-
- // History Query.
- this.create_query("History", leftPaneRoot,
- "place:type=" +
- Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY +
- "&sort=" +
- Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
-
- // Downloads.
- this.create_query("Downloads", leftPaneRoot,
- "place:transition=" +
- Ci.nsINavHistoryService.TRANSITION_DOWNLOAD +
- "&sort=" +
- Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING);
-
- // Tags Query.
- this.create_query("Tags", leftPaneRoot,
- "place:type=" +
- Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
- "&sort=" +
- Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING);
-
- // All Bookmarks Folder.
- allBookmarksId = this.create_folder("AllBookmarks", leftPaneRoot, false);
-
- // All Bookmarks->Bookmarks Toolbar Query.
- this.create_query("BookmarksToolbar", allBookmarksId,
- "place:folder=TOOLBAR");
-
- // All Bookmarks->Bookmarks Menu Query.
- this.create_query("BookmarksMenu", allBookmarksId,
- "place:folder=BOOKMARKS_MENU");
-
- // All Bookmarks->Unfiled Bookmarks Query.
- this.create_query("UnfiledBookmarks", allBookmarksId,
- "place:folder=UNFILED_BOOKMARKS");
- }
- };
- bs.runInBatchMode(callback, null);
- // Maybe: PlacesUtils.bookmarks.runInBatchMode(callback, null); ?
-
- delete this.leftPaneFolderId;
- return this.leftPaneFolderId = leftPaneRoot;
- },
-
- /**
- * Get the folder id for the organizer left-pane folder.
- */
- get allBookmarksFolderId() {
- // ensure the left-pane root is initialized;
- this.leftPaneFolderId;
- delete this.allBookmarksFolderId;
- return this.allBookmarksFolderId = this.leftPaneQueries["AllBookmarks"];
- },
-
- /**
- * If an item is a left-pane query, returns the name of the query
- * or an empty string if not.
- *
- * @param aItemId id of a container
- * @returns the name of the query, or empty string if not a left-pane query
- */
- getLeftPaneQueryNameFromId: function PUIU_getLeftPaneQueryNameFromId(aItemId) {
- var queryName = "";
- // If the let pane hasn't been built, use the annotation service
- // directly, to avoid building the left pane too early.
- if (Object.getOwnPropertyDescriptor(this, "leftPaneFolderId").value === undefined) {
- try {
- queryName = PlacesUtils.annotations.
- getItemAnnotation(aItemId, this.ORGANIZER_QUERY_ANNO);
- }
- catch (ex) {
- // doesn't have the annotation
- queryName = "";
- }
- }
- else {
- // If the left pane has already been built, use the name->id map
- // cached in PlacesUIUtils.
- for (let [name, id] in Iterator(this.leftPaneQueries)) {
- if (aItemId == id)
- queryName = name;
- }
- }
- return queryName;
- },
-
- /**
- * Returns the passed URL with a #moz-resolution fragment
- * for the specified dimensions and devicePixelRatio.
- *
- * @param aWindow
- * A window from where we want to get the device
- * pixel Ratio
- *
- * @param aURL
- * The URL where we should add the fragment
- *
- * @param aWidth
- * The target image width
- *
- * @param aHeight
- * The target image height
- *
- * @return The URL with the fragment at the end
- */
- getImageURLForResolution:
- function PUIU_getImageURLForResolution(aWindow, aURL, aWidth, aHeight) {
- return aURL;
- }
-};
-
-XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "RDF",
- "@mozilla.org/rdf/rdf-service;1",
- "nsIRDFService");
-
-XPCOMUtils.defineLazyGetter(PlacesUIUtils, "localStore", function() {
- return PlacesUIUtils.RDF.GetDataSource("rdf:local-store");
-});
-
-XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() {
- return Services.prefs.getComplexValue("intl.ellipsis",
- Ci.nsIPrefLocalizedString).data;
-});
-
-XPCOMUtils.defineLazyServiceGetter(this, "URIFixup",
- "@mozilla.org/docshell/urifixup;1",
- "nsIURIFixup");
-
-XPCOMUtils.defineLazyGetter(this, "bundle", function() {
- const PLACES_STRING_BUNDLE_URI =
- "chrome://browser/locale/places/places.properties";
- return Cc["@mozilla.org/intl/stringbundle;1"].
- getService(Ci.nsIStringBundleService).
- createBundle(PLACES_STRING_BUNDLE_URI);
-});
-
-XPCOMUtils.defineLazyServiceGetter(this, "focusManager",
- "@mozilla.org/focus-manager;1",
- "nsIFocusManager");
-
-/**
- * This is a compatibility shim for old PUIU.ptm users.
- *
- * If you're looking for transactions and writing new code using them, directly
- * use the transactions objects exported by the PlacesUtils.jsm module.
- *
- * This object will be removed once enough users are converted to the new API.
- */
-XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ptm", function() {
- // Ensure PlacesUtils is imported in scope.
- PlacesUtils;
-
- return {
- aggregateTransactions: function(aName, aTransactions)
- new PlacesAggregatedTransaction(aName, aTransactions),
-
- createFolder: function(aName, aContainer, aIndex, aAnnotations,
- aChildItemsTransactions)
- new PlacesCreateFolderTransaction(aName, aContainer, aIndex, aAnnotations,
- aChildItemsTransactions),
-
- createItem: function(aURI, aContainer, aIndex, aTitle, aKeyword,
- aAnnotations, aChildTransactions)
- new PlacesCreateBookmarkTransaction(aURI, aContainer, aIndex, aTitle,
- aKeyword, aAnnotations,
- aChildTransactions),
-
- createSeparator: function(aContainer, aIndex)
- new PlacesCreateSeparatorTransaction(aContainer, aIndex),
-
- createLivemark: function(aFeedURI, aSiteURI, aName, aContainer, aIndex,
- aAnnotations)
- new PlacesCreateLivemarkTransaction(aFeedURI, aSiteURI, aName, aContainer,
- aIndex, aAnnotations),
-
- moveItem: function(aItemId, aNewContainer, aNewIndex)
- new PlacesMoveItemTransaction(aItemId, aNewContainer, aNewIndex),
-
- removeItem: function(aItemId)
- new PlacesRemoveItemTransaction(aItemId),
-
- editItemTitle: function(aItemId, aNewTitle)
- new PlacesEditItemTitleTransaction(aItemId, aNewTitle),
-
- editBookmarkURI: function(aItemId, aNewURI)
- new PlacesEditBookmarkURITransaction(aItemId, aNewURI),
-
- setItemAnnotation: function(aItemId, aAnnotationObject)
- new PlacesSetItemAnnotationTransaction(aItemId, aAnnotationObject),
-
- setPageAnnotation: function(aURI, aAnnotationObject)
- new PlacesSetPageAnnotationTransaction(aURI, aAnnotationObject),
-
- editBookmarkKeyword: function(aItemId, aNewKeyword)
- new PlacesEditBookmarkKeywordTransaction(aItemId, aNewKeyword),
-
- editBookmarkPostData: function(aItemId, aPostData)
- new PlacesEditBookmarkPostDataTransaction(aItemId, aPostData),
-
- editLivemarkSiteURI: function(aLivemarkId, aSiteURI)
- new PlacesEditLivemarkSiteURITransaction(aLivemarkId, aSiteURI),
-
- editLivemarkFeedURI: function(aLivemarkId, aFeedURI)
- new PlacesEditLivemarkFeedURITransaction(aLivemarkId, aFeedURI),
-
- editItemDateAdded: function(aItemId, aNewDateAdded)
- new PlacesEditItemDateAddedTransaction(aItemId, aNewDateAdded),
-
- editItemLastModified: function(aItemId, aNewLastModified)
- new PlacesEditItemLastModifiedTransaction(aItemId, aNewLastModified),
-
- sortFolderByName: function(aFolderId)
- new PlacesSortFolderByNameTransaction(aFolderId),
-
- tagURI: function(aURI, aTags)
- new PlacesTagURITransaction(aURI, aTags),
-
- untagURI: function(aURI, aTags)
- new PlacesUntagURITransaction(aURI, aTags),
-
- /**
- * Transaction for setting/unsetting Load-in-sidebar annotation.
- *
- * @param aBookmarkId
- * id of the bookmark where to set Load-in-sidebar annotation.
- * @param aLoadInSidebar
- * boolean value.
- * @returns nsITransaction object.
- */
- setLoadInSidebar: function(aItemId, aLoadInSidebar)
- {
- let annoObj = { name: PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
- type: Ci.nsIAnnotationService.TYPE_INT32,
- flags: 0,
- value: aLoadInSidebar,
- expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
- return new PlacesSetItemAnnotationTransaction(aItemId, annoObj);
- },
-
- /**
- * Transaction for editing the description of a bookmark or a folder.
- *
- * @param aItemId
- * id of the item to edit.
- * @param aDescription
- * new description.
- * @returns nsITransaction object.
- */
- editItemDescription: function(aItemId, aDescription)
- {
- let annoObj = { name: PlacesUIUtils.DESCRIPTION_ANNO,
- type: Ci.nsIAnnotationService.TYPE_STRING,
- flags: 0,
- value: aDescription,
- expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
- return new PlacesSetItemAnnotationTransaction(aItemId, annoObj);
- },
-
- ////////////////////////////////////////////////////////////////////////////
- //// nsITransactionManager forwarders.
-
- beginBatch: function()
- PlacesUtils.transactionManager.beginBatch(null),
-
- endBatch: function()
- PlacesUtils.transactionManager.endBatch(false),
-
- doTransaction: function(txn)
- PlacesUtils.transactionManager.doTransaction(txn),
-
- undoTransaction: function()
- PlacesUtils.transactionManager.undoTransaction(),
-
- redoTransaction: function()
- PlacesUtils.transactionManager.redoTransaction(),
-
- get numberOfUndoItems()
- PlacesUtils.transactionManager.numberOfUndoItems,
- get numberOfRedoItems()
- PlacesUtils.transactionManager.numberOfRedoItems,
- get maxTransactionCount()
- PlacesUtils.transactionManager.maxTransactionCount,
- set maxTransactionCount(val)
- PlacesUtils.transactionManager.maxTransactionCount = val,
-
- clear: function()
- PlacesUtils.transactionManager.clear(),
-
- peekUndoStack: function()
- PlacesUtils.transactionManager.peekUndoStack(),
-
- peekRedoStack: function()
- PlacesUtils.transactionManager.peekRedoStack(),
-
- getUndoStack: function()
- PlacesUtils.transactionManager.getUndoStack(),
-
- getRedoStack: function()
- PlacesUtils.transactionManager.getRedoStack(),
-
- AddListener: function(aListener)
- PlacesUtils.transactionManager.AddListener(aListener),
-
- RemoveListener: function(aListener)
- PlacesUtils.transactionManager.RemoveListener(aListener)
- }
-});
diff --git a/components/places/content/bookmarkProperties.js b/components/places/content/bookmarkProperties.js
deleted file mode 100644
index e1d1077..0000000
--- a/components/places/content/bookmarkProperties.js
+++ /dev/null
@@ -1,675 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-/**
- * The panel is initialized based on data given in the js object passed
- * as window.arguments[0]. The object must have the following fields set:
- * @ action (String). Possible values:
- * - "add" - for adding a new item.
- * @ type (String). Possible values:
- * - "bookmark"
- * @ loadBookmarkInSidebar - optional, the default state for the
- * "Load this bookmark in the sidebar" field.
- * - "folder"
- * @ URIList (Array of nsIURI objects) - optional, list of uris to
- * be bookmarked under the new folder.
- * - "livemark"
- * @ uri (nsIURI object) - optional, the default uri for the new item.
- * The property is not used for the "folder with items" type.
- * @ title (String) - optional, the default title for the new item.
- * @ description (String) - optional, the default description for the new
- * item.
- * @ defaultInsertionPoint (InsertionPoint JS object) - optional, the
- * default insertion point for the new item.
- * @ keyword (String) - optional, the default keyword for the new item.
- * @ postData (String) - optional, POST data to accompany the keyword.
- * @ charSet (String) - optional, character-set to accompany the keyword.
- * Notes:
- * 1) If |uri| is set for a bookmark/livemark item and |title| isn't,
- * the dialog will query the history tables for the title associated
- * with the given uri. If the dialog is set to adding a folder with
- * bookmark items under it (see URIList), a default static title is
- * used ("[Folder Name]").
- * 2) The index field of the default insertion point is ignored if
- * the folder picker is shown.
- * - "edit" - for editing a bookmark item or a folder.
- * @ type (String). Possible values:
- * - "bookmark"
- * @ itemId (Integer) - the id of the bookmark item.
- * - "folder" (also applies to livemarks)
- * @ itemId (Integer) - the id of the folder.
- * @ hiddenRows (Strings array) - optional, list of rows to be hidden
- * regardless of the item edited or added by the dialog.
- * Possible values:
- * - "title"
- * - "location"
- * - "description"
- * - "keyword"
- * - "tags"
- * - "loadInSidebar"
- * - "feedLocation"
- * - "siteLocation"
- * - "folderPicker" - hides both the tree and the menu.
- * @ readOnly (Boolean) - optional, states if the panel should be read-only
- *
- * window.arguments[0].performed is set to true if any transaction has
- * been performed by the dialog.
- */
-
-Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
-XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
- "resource://gre/modules/PrivateBrowsingUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Task",
- "resource://gre/modules/Task.jsm");
-
-const BOOKMARK_ITEM = 0;
-const BOOKMARK_FOLDER = 1;
-const LIVEMARK_CONTAINER = 2;
-
-const ACTION_EDIT = 0;
-const ACTION_ADD = 1;
-
-var elementsHeight = new Map();
-
-var BookmarkPropertiesPanel = {
-
- /** UI Text Strings */
- __strings: null,
- get _strings() {
- if (!this.__strings) {
- this.__strings = document.getElementById("stringBundle");
- }
- return this.__strings;
- },
-
- _action: null,
- _itemType: null,
- _itemId: -1,
- _uri: null,
- _loadInSidebar: false,
- _title: "",
- _description: "",
- _URIs: [],
- _keyword: "",
- _postData: null,
- _charSet: "",
- _feedURI: null,
- _siteURI: null,
-
- _defaultInsertionPoint: null,
- _hiddenRows: [],
- _batching: false,
- _readOnly: false,
-
- /**
- * This method returns the correct label for the dialog's "accept"
- * button based on the variant of the dialog.
- */
- _getAcceptLabel: function BPP__getAcceptLabel() {
- if (this._action == ACTION_ADD) {
- if (this._URIs.length)
- return this._strings.getString("dialogAcceptLabelAddMulti");
-
- if (this._itemType == LIVEMARK_CONTAINER)
- return this._strings.getString("dialogAcceptLabelAddLivemark");
-
- if (this._dummyItem || this._loadInSidebar)
- return this._strings.getString("dialogAcceptLabelAddItem");
-
- return this._strings.getString("dialogAcceptLabelSaveItem");
- }
- return this._strings.getString("dialogAcceptLabelEdit");
- },
-
- /**
- * This method returns the correct title for the current variant
- * of this dialog.
- */
- _getDialogTitle: function BPP__getDialogTitle() {
- if (this._action == ACTION_ADD) {
- if (this._itemType == BOOKMARK_ITEM)
- return this._strings.getString("dialogTitleAddBookmark");
- if (this._itemType == LIVEMARK_CONTAINER)
- return this._strings.getString("dialogTitleAddLivemark");
-
- // add folder
- NS_ASSERT(this._itemType == BOOKMARK_FOLDER, "Unknown item type");
- if (this._URIs.length)
- return this._strings.getString("dialogTitleAddMulti");
-
- return this._strings.getString("dialogTitleAddFolder");
- }
- if (this._action == ACTION_EDIT) {
- return this._strings.getFormattedString("dialogTitleEdit", [this._title]);
- }
- return "";
- },
-
- /**
- * Determines the initial data for the item edited or added by this dialog
- */
- _determineItemInfo: function BPP__determineItemInfo() {
- var dialogInfo = window.arguments[0];
- this._action = dialogInfo.action == "add" ? ACTION_ADD : ACTION_EDIT;
- this._hiddenRows = dialogInfo.hiddenRows ? dialogInfo.hiddenRows : [];
- if (this._action == ACTION_ADD) {
- NS_ASSERT("type" in dialogInfo, "missing type property for add action");
-
- if ("title" in dialogInfo)
- this._title = dialogInfo.title;
-
- if ("defaultInsertionPoint" in dialogInfo) {
- this._defaultInsertionPoint = dialogInfo.defaultInsertionPoint;
- }
- else
- this._defaultInsertionPoint =
- new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
- PlacesUtils.bookmarks.DEFAULT_INDEX,
- Ci.nsITreeView.DROP_ON);
-
- switch (dialogInfo.type) {
- case "bookmark":
- this._itemType = BOOKMARK_ITEM;
- if ("uri" in dialogInfo) {
- NS_ASSERT(dialogInfo.uri instanceof Ci.nsIURI,
- "uri property should be a uri object");
- this._uri = dialogInfo.uri;
- if (typeof(this._title) != "string") {
- this._title = this._getURITitleFromHistory(this._uri) ||
- this._uri.spec;
- }
- }
- else {
- this._uri = PlacesUtils._uri("about:blank");
- this._title = this._strings.getString("newBookmarkDefault");
- this._dummyItem = true;
- }
-
- if ("loadBookmarkInSidebar" in dialogInfo)
- this._loadInSidebar = dialogInfo.loadBookmarkInSidebar;
-
- if ("keyword" in dialogInfo) {
- this._keyword = dialogInfo.keyword;
- this._isAddKeywordDialog = true;
- if ("postData" in dialogInfo)
- this._postData = dialogInfo.postData;
- if ("charSet" in dialogInfo)
- this._charSet = dialogInfo.charSet;
- }
- break;
-
- case "folder":
- this._itemType = BOOKMARK_FOLDER;
- if (!this._title) {
- if ("URIList" in dialogInfo) {
- this._title = this._strings.getString("bookmarkAllTabsDefault");
- this._URIs = dialogInfo.URIList;
- }
- else
- this._title = this._strings.getString("newFolderDefault");
- this._dummyItem = true;
- }
- break;
-
- case "livemark":
- this._itemType = LIVEMARK_CONTAINER;
- if ("feedURI" in dialogInfo)
- this._feedURI = dialogInfo.feedURI;
- if ("siteURI" in dialogInfo)
- this._siteURI = dialogInfo.siteURI;
-
- if (!this._title) {
- if (this._feedURI) {
- this._title = this._getURITitleFromHistory(this._feedURI) ||
- this._feedURI.spec;
- }
- else
- this._title = this._strings.getString("newLivemarkDefault");
- }
- }
-
- if ("description" in dialogInfo)
- this._description = dialogInfo.description;
- }
- else { // edit
- NS_ASSERT("itemId" in dialogInfo);
- this._itemId = dialogInfo.itemId;
- this._title = PlacesUtils.bookmarks.getItemTitle(this._itemId);
- this._readOnly = !!dialogInfo.readOnly;
-
- switch (dialogInfo.type) {
- case "bookmark":
- this._itemType = BOOKMARK_ITEM;
-
- this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
- // keyword
- this._keyword = PlacesUtils.bookmarks
- .getKeywordForBookmark(this._itemId);
- // Load In Sidebar
- this._loadInSidebar = PlacesUtils.annotations
- .itemHasAnnotation(this._itemId,
- PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO);
- break;
-
- case "folder":
- this._itemType = BOOKMARK_FOLDER;
- PlacesUtils.livemarks.getLivemark({ id: this._itemId })
- .then(aLivemark => {
- this._itemType = LIVEMARK_CONTAINER;
- this._feedURI = aLivemark.feedURI;
- this._siteURI = aLivemark.siteURI;
- this._fillEditProperties();
-
- let acceptButton = document.documentElement.getButton("accept");
- acceptButton.disabled = !this._inputIsValid();
-
- let newHeight = window.outerHeight +
- this._element("descriptionField").boxObject.height;
- window.resizeTo(window.outerWidth, newHeight);
- }, () => undefined);
-
- break;
- }
-
- // Description
- if (PlacesUtils.annotations
- .itemHasAnnotation(this._itemId, PlacesUIUtils.DESCRIPTION_ANNO)) {
- this._description = PlacesUtils.annotations
- .getItemAnnotation(this._itemId,
- PlacesUIUtils.DESCRIPTION_ANNO);
- }
- }
- },
-
- /**
- * This method returns the title string corresponding to a given URI.
- * If none is available from the bookmark service (probably because
- * the given URI doesn't appear in bookmarks or history), we synthesize
- * a title from the first 100 characters of the URI.
- *
- * @param aURI
- * nsIURI object for which we want the title
- *
- * @returns a title string
- */
- _getURITitleFromHistory: function BPP__getURITitleFromHistory(aURI) {
- NS_ASSERT(aURI instanceof Ci.nsIURI);
-
- // get the title from History
- return PlacesUtils.history.getPageTitle(aURI);
- },
-
- /**
- * This method should be called by the onload of the Bookmark Properties
- * dialog to initialize the state of the panel.
- */
- onDialogLoad: Task.async(function* BPP_onDialogLoad() {
- this._determineItemInfo();
-
- document.title = this._getDialogTitle();
- var acceptButton = document.documentElement.getButton("accept");
- acceptButton.label = this._getAcceptLabel();
-
- // Do not use sizeToContent, otherwise, due to bug 90276, the dialog will
- // grow at every opening.
- // Since elements can be uncollapsed asynchronously, we must observe their
- // mutations and resize the dialog using a cached element size.
- this._height = window.outerHeight;
- this._mutationObserver = new MutationObserver(mutations => {
- for (let mutation of mutations) {
- let target = mutation.target;
- let id = target.id;
- if (!/^editBMPanel_.*(Row|Checkbox)$/.test(id))
- continue;
-
- let collapsed = target.getAttribute("collapsed") === "true";
- let wasCollapsed = mutation.oldValue === "true";
- if (collapsed == wasCollapsed)
- continue;
-
- if (collapsed) {
- this._height -= elementsHeight.get(id);
- elementsHeight.delete(id);
- } else {
- elementsHeight.set(id, target.boxObject.height);
- this._height += elementsHeight.get(id);
- }
- window.resizeTo(window.outerWidth, this._height);
- }
- });
-
- this._mutationObserver.observe(document,
- { subtree: true,
- attributeOldValue: true,
- attributeFilter: ["collapsed"] });
-
- // Some controls are flexible and we want to update their cached size when
- // the dialog is resized.
- window.addEventListener("resize", this);
-
- this._beginBatch();
-
- switch (this._action) {
- case ACTION_EDIT:
- this._fillEditProperties();
- acceptButton.disabled = this._readOnly;
- break;
- case ACTION_ADD:
- yield this._fillAddProperties();
- // if this is an uri related dialog disable accept button until
- // the user fills an uri value.
- if (this._itemType == BOOKMARK_ITEM)
- acceptButton.disabled = !this._inputIsValid();
- break;
- }
-
- if (!this._readOnly) {
- // Listen on uri fields to enable accept button if input is valid
- if (this._itemType == BOOKMARK_ITEM) {
- this._element("locationField")
- .addEventListener("input", this, false);
- if (this._isAddKeywordDialog) {
- this._element("keywordField")
- .addEventListener("input", this, false);
- }
- }
- else if (this._itemType == LIVEMARK_CONTAINER) {
- this._element("feedLocationField")
- .addEventListener("input", this, false);
- this._element("siteLocationField")
- .addEventListener("input", this, false);
- }
- }
-
- // Ensure the Name Picker textbox is focused on load
- var namePickerElem = document.getElementById('editBMPanel_namePicker');
- namePickerElem.focus();
- namePickerElem.select();
- }),
-
- // nsIDOMEventListener
- handleEvent: function BPP_handleEvent(aEvent) {
- var target = aEvent.target;
- switch (aEvent.type) {
- case "input":
- if (target.id == "editBMPanel_locationField" ||
- target.id == "editBMPanel_feedLocationField" ||
- target.id == "editBMPanel_siteLocationField" ||
- target.id == "editBMPanel_keywordField") {
- // Check uri fields to enable accept button if input is valid
- document.documentElement
- .getButton("accept").disabled = !this._inputIsValid();
- }
- break;
- case "resize":
- for (let [id, oldHeight] of elementsHeight) {
- let newHeight = document.getElementById(id).boxObject.height;
- this._height += - oldHeight + newHeight;
- elementsHeight.set(id, newHeight);
- }
- break;
- }
- },
-
- _beginBatch: function BPP__beginBatch() {
- if (this._batching)
- return;
-
- PlacesUtils.transactionManager.beginBatch(null);
- this._batching = true;
- },
-
- _endBatch: function BPP__endBatch() {
- if (!this._batching)
- return;
-
- PlacesUtils.transactionManager.endBatch(false);
- this._batching = false;
- },
-
- _fillEditProperties: function BPP__fillEditProperties() {
- gEditItemOverlay.initPanel(this._itemId,
- { hiddenRows: this._hiddenRows,
- forceReadOnly: this._readOnly });
- },
-
- _fillAddProperties: Task.async(function* BPP__fillAddProperties() {
- yield this._createNewItem();
- // Edit the new item
- gEditItemOverlay.initPanel(this._itemId,
- { hiddenRows: this._hiddenRows });
- // Empty location field if the uri is about:blank, this way inserting a new
- // url will be easier for the user, Accept button will be automatically
- // disabled by the input listener until the user fills the field.
- var locationField = this._element("locationField");
- if (locationField.value == "about:blank")
- locationField.value = "";
- }),
-
- // nsISupports
- QueryInterface: function BPP_QueryInterface(aIID) {
- if (aIID.equals(Ci.nsIDOMEventListener) ||
- aIID.equals(Ci.nsISupports))
- return this;
-
- throw Cr.NS_NOINTERFACE;
- },
-
- _element: function BPP__element(aID) {
- return document.getElementById("editBMPanel_" + aID);
- },
-
- onDialogUnload: function BPP_onDialogUnload() {
- // gEditItemOverlay does not exist anymore here, so don't rely on it.
- this._mutationObserver.disconnect();
- delete this._mutationObserver;
-
- window.removeEventListener("resize", this);
-
- // Calling removeEventListener with arguments which do not identify any
- // currently registered EventListener on the EventTarget has no effect.
- this._element("locationField")
- .removeEventListener("input", this, false);
- this._element("feedLocationField")
- .removeEventListener("input", this, false);
- this._element("siteLocationField")
- .removeEventListener("input", this, false);
- },
-
- onDialogAccept: function BPP_onDialogAccept() {
- // We must blur current focused element to save its changes correctly
- document.commandDispatcher.focusedElement.blur();
- // The order here is important! We have to uninit the panel first, otherwise
- // late changes could force it to commit more transactions.
- gEditItemOverlay.uninitPanel(true);
- this._endBatch();
- window.arguments[0].performed = true;
- },
-
- onDialogCancel: function BPP_onDialogCancel() {
- // The order here is important! We have to uninit the panel first, otherwise
- // changes done as part of Undo may change the panel contents and by
- // that force it to commit more transactions.
- gEditItemOverlay.uninitPanel(true);
- this._endBatch();
- PlacesUtils.transactionManager.undoTransaction();
- window.arguments[0].performed = false;
- },
-
- /**
- * This method checks to see if the input fields are in a valid state.
- *
- * @returns true if the input is valid, false otherwise
- */
- _inputIsValid: function BPP__inputIsValid() {
- if (this._itemType == BOOKMARK_ITEM &&
- !this._containsValidURI("locationField"))
- return false;
- if (this._isAddKeywordDialog && !this._element("keywordField").value.length)
- return false;
-
- return true;
- },
-
- /**
- * Determines whether the XUL textbox with the given ID contains a
- * string that can be converted into an nsIURI.
- *
- * @param aTextboxID
- * the ID of the textbox element whose contents we'll test
- *
- * @returns true if the textbox contains a valid URI string, false otherwise
- */
- _containsValidURI: function BPP__containsValidURI(aTextboxID) {
- try {
- var value = this._element(aTextboxID).value;
- if (value) {
- PlacesUIUtils.createFixedURI(value);
- return true;
- }
- } catch (e) { }
- return false;
- },
-
- /**
- * [New Item Mode] Get the insertion point details for the new item, given
- * dialog state and opening arguments.
- *
- * The container-identifier and insertion-index are returned separately in
- * the form of [containerIdentifier, insertionIndex]
- */
- _getInsertionPointDetails: function BPP__getInsertionPointDetails() {
- var containerId = this._defaultInsertionPoint.itemId;
- var indexInContainer = this._defaultInsertionPoint.index;
-
- return [containerId, indexInContainer];
- },
-
- /**
- * Returns a transaction for creating a new bookmark item representing the
- * various fields and opening arguments of the dialog.
- */
- _getCreateNewBookmarkTransaction:
- function BPP__getCreateNewBookmarkTransaction(aContainer, aIndex) {
- var annotations = [];
- var childTransactions = [];
-
- if (this._description) {
- let annoObj = { name : PlacesUIUtils.DESCRIPTION_ANNO,
- type : Ci.nsIAnnotationService.TYPE_STRING,
- flags : 0,
- value : this._description,
- expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
- let editItemTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj);
- childTransactions.push(editItemTxn);
- }
-
- if (this._loadInSidebar) {
- let annoObj = { name : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO,
- value : true };
- let setLoadTxn = new PlacesSetItemAnnotationTransaction(-1, annoObj);
- childTransactions.push(setLoadTxn);
- }
-
- if (this._postData) {
- let postDataTxn = new PlacesEditBookmarkPostDataTransaction(-1, this._postData);
- childTransactions.push(postDataTxn);
- }
-
- //XXX TODO: this should be in a transaction!
- if (this._charSet && !PrivateBrowsingUtils.isWindowPrivate(window))
- PlacesUtils.setCharsetForURI(this._uri, this._charSet);
-
- let createTxn = new PlacesCreateBookmarkTransaction(this._uri,
- aContainer,
- aIndex,
- this._title,
- this._keyword,
- annotations,
- childTransactions);
-
- return new PlacesAggregatedTransaction(this._getDialogTitle(),
- [createTxn]);
- },
-
- /**
- * Returns a childItems-transactions array representing the URIList with
- * which the dialog has been opened.
- */
- _getTransactionsForURIList: function BPP__getTransactionsForURIList() {
- var transactions = [];
- for (var i = 0; i < this._URIs.length; ++i) {
- var uri = this._URIs[i];
- var title = this._getURITitleFromHistory(uri);
- var createTxn = new PlacesCreateBookmarkTransaction(uri, -1,
- PlacesUtils.bookmarks.DEFAULT_INDEX,
- title);
- transactions.push(createTxn);
- }
- return transactions;
- },
-
- /**
- * Returns a transaction for creating a new folder item representing the
- * various fields and opening arguments of the dialog.
- */
- _getCreateNewFolderTransaction:
- function BPP__getCreateNewFolderTransaction(aContainer, aIndex) {
- var annotations = [];
- var childItemsTransactions;
- if (this._URIs.length)
- childItemsTransactions = this._getTransactionsForURIList();
-
- if (this._description)
- annotations.push(this._getDescriptionAnnotation(this._description));
-
- return new PlacesCreateFolderTransaction(this._title, aContainer,
- aIndex, annotations,
- childItemsTransactions);
- },
-
- /**
- * Returns a transaction for creating a new live-bookmark item representing
- * the various fields and opening arguments of the dialog.
- */
- _getCreateNewLivemarkTransaction:
- function BPP__getCreateNewLivemarkTransaction(aContainer, aIndex) {
- return new PlacesCreateLivemarkTransaction(this._feedURI, this._siteURI,
- this._title,
- aContainer, aIndex);
- },
-
- /**
- * Dialog-accept code-path for creating a new item (any type)
- */
- _createNewItem: Task.async(function* BPP__getCreateItemTransaction() {
- var [container, index] = this._getInsertionPointDetails();
- var txn;
-
- switch (this._itemType) {
- case BOOKMARK_FOLDER:
- txn = this._getCreateNewFolderTransaction(container, index);
- break;
- case LIVEMARK_CONTAINER:
- txn = this._getCreateNewLivemarkTransaction(container, index);
- break;
- default: // BOOKMARK_ITEM
- txn = this._getCreateNewBookmarkTransaction(container, index);
- }
-
- PlacesUtils.transactionManager.doTransaction(txn);
- // This is a temporary hack until we use PlacesTransactions.jsm
- if (txn._promise) {
- yield txn._promise;
- }
-
- let folderGuid = yield PlacesUtils.promiseItemGuid(container);
- let bm = yield PlacesUtils.bookmarks.fetch({
- parentGuid: folderGuid,
- index: index
- });
- this._itemId = yield PlacesUtils.promiseItemId(bm.guid);
- })
-};
diff --git a/components/places/content/bookmarkProperties.xul b/components/places/content/bookmarkProperties.xul
deleted file mode 100644
index 2c04f8b..0000000
--- a/components/places/content/bookmarkProperties.xul
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0"?>
-
-<!-- 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/. -->
-
-<?xml-stylesheet href="chrome://global/skin/"?>
-<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
-<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
-<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
-
-<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
-<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>
-
-<!DOCTYPE dialog [
- <!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd">
- %editBookmarkOverlayDTD;
-]>
-
-<dialog id="bookmarkproperties"
- buttons="accept, cancel"
- buttoniconaccept="save"
- ondialogaccept="BookmarkPropertiesPanel.onDialogAccept();"
- ondialogcancel="BookmarkPropertiesPanel.onDialogCancel();"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- onload="BookmarkPropertiesPanel.onDialogLoad();"
- onunload="BookmarkPropertiesPanel.onDialogUnload();"
- style="min-width: 30em;"
- persist="screenX screenY width">
-
- <stringbundleset id="stringbundleset">
- <stringbundle id="stringBundle"
- src="chrome://browser/locale/places/bookmarkProperties.properties"/>
- </stringbundleset>
-
- <script type="application/javascript"
- src="chrome://browser/content/places/editBookmarkOverlay.js"/>
- <script type="application/javascript"
- src="chrome://browser/content/places/bookmarkProperties.js"/>
-
-<vbox id="editBookmarkPanelContent"/>
-
-</dialog>
diff --git a/components/places/content/bookmarksPanel.js b/components/places/content/bookmarksPanel.js
deleted file mode 100644
index c964bd0..0000000
--- a/components/places/content/bookmarksPanel.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-function init() {
- document.getElementById("bookmarks-view").place =
- "place:queryType=1&folder=" + window.top.PlacesUIUtils.allBookmarksFolderId;
-}
-
-function searchBookmarks(aSearchString) {
- var tree = document.getElementById('bookmarks-view');
- if (!aSearchString)
- tree.place = tree.place;
- else
- tree.applyFilter(aSearchString,
- [PlacesUtils.bookmarksMenuFolderId,
- PlacesUtils.unfiledBookmarksFolderId,
- PlacesUtils.toolbarFolderId]);
-}
-
-window.addEventListener("SidebarFocused",
- function()
- document.getElementById("search-box").focus(),
- false);
diff --git a/components/places/content/bookmarksPanel.xul b/components/places/content/bookmarksPanel.xul
deleted file mode 100644
index 45744bb..0000000
--- a/components/places/content/bookmarksPanel.xul
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0"?> <!-- -*- Mode: SGML; indent-tabs-mode: nil; -*- -->
-<!-- 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/. -->
-
-<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
-<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
-<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
-<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
-
-<!DOCTYPE page SYSTEM "chrome://browser/locale/places/places.dtd">
-
-<page id="bookmarksPanel"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- onload="init();"
- onunload="SidebarUtils.setMouseoverURL('');">
-
- <script type="application/javascript"
- src="chrome://browser/content/bookmarks/sidebarUtils.js"/>
- <script type="application/javascript"
- src="chrome://browser/content/bookmarks/bookmarksPanel.js"/>
-
- <commandset id="placesCommands"/>
- <commandset id="editMenuCommands"/>
- <keyset id="placesCommandKeys"/>
- <menupopup id="placesContext"/>
-
- <!-- Bookmarks and history tooltip -->
- <tooltip id="bhTooltip"/>
-
- <hbox id="sidebar-search-container" align="center">
- <label id="sidebar-search-label"
- value="&search.label;" accesskey="&search.accesskey;" control="search-box"/>
- <textbox id="search-box" flex="1" type="search" class="compact"
- aria-controls="bookmarks-view"
- oncommand="searchBookmarks(this.value);"/>
- </hbox>
-
- <tree id="bookmarks-view" class="sidebar-placesTree" type="places"
- flex="1"
- hidecolumnpicker="true"
- context="placesContext"
- onkeypress="SidebarUtils.handleTreeKeyPress(event);"
- onclick="SidebarUtils.handleTreeClick(this, event, true);"
- onmousemove="SidebarUtils.handleTreeMouseMove(event);"
- onmouseout="SidebarUtils.setMouseoverURL('');">
- <treecols>
- <treecol id="title" flex="1" primary="true" hideheader="true"/>
- </treecols>
- <treechildren id="bookmarks-view-children" view="bookmarks-view"
- class="sidebar-placesTreechildren" flex="1" tooltip="bhTooltip"/>
- </tree>
-</page>
diff --git a/components/places/content/browserPlacesViews.js b/components/places/content/browserPlacesViews.js
deleted file mode 100644
index 8b90dd2..0000000
--- a/components/places/content/browserPlacesViews.js
+++ /dev/null
@@ -1,1754 +0,0 @@
-/* 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/. */
-
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
-
-/**
- * The base view implements everything that's common to the toolbar and
- * menu views.
- */
-function PlacesViewBase(aPlace) {
- this.place = aPlace;
- this._controller = new PlacesController(this);
- this._viewElt.controllers.appendController(this._controller);
-}
-
-PlacesViewBase.prototype = {
- // The xul element that holds the entire view.
- _viewElt: null,
- get viewElt() this._viewElt,
-
- get associatedElement() this._viewElt,
-
- get controllers() this._viewElt.controllers,
-
- // The xul element that represents the root container.
- _rootElt: null,
-
- // Set to true for views that are represented by native widgets (i.e.
- // the native mac menu).
- _nativeView: false,
-
- QueryInterface: XPCOMUtils.generateQI(
- [Components.interfaces.nsINavHistoryResultObserver,
- Components.interfaces.nsISupportsWeakReference]),
-
- _place: "",
- get place() this._place,
- set place(val) {
- this._place = val;
-
- let history = PlacesUtils.history;
- let queries = { }, options = { };
- history.queryStringToQueries(val, queries, { }, options);
- if (!queries.value.length)
- queries.value = [history.getNewQuery()];
-
- let result = history.executeQueries(queries.value, queries.value.length,
- options.value);
- result.addObserver(this, false);
- return val;
- },
-
- _result: null,
- get result() this._result,
- set result(val) {
- if (this._result == val)
- return val;
-
- if (this._result) {
- this._result.removeObserver(this);
- this._resultNode.containerOpen = false;
- }
-
- if (this._rootElt.localName == "menupopup")
- this._rootElt._built = false;
-
- this._result = val;
- if (val) {
- this._resultNode = val.root;
- this._rootElt._placesNode = this._resultNode;
- this._domNodes = new Map();
- this._domNodes.set(this._resultNode, this._rootElt);
-
- // This calls _rebuild through invalidateContainer.
- this._resultNode.containerOpen = true;
- }
- else {
- this._resultNode = null;
- delete this._domNodes;
- }
-
- return val;
- },
-
- /**
- * Gets the DOM node used for the given places node.
- *
- * @param aPlacesNode
- * a places result node.
- * @throws if there is no DOM node set for aPlacesNode.
- */
- _getDOMNodeForPlacesNode:
- function PVB__getDOMNodeForPlacesNode(aPlacesNode) {
- let node = this._domNodes.get(aPlacesNode, null);
- if (!node) {
- throw new Error("No DOM node set for aPlacesNode.\nnode.type: " +
- aPlacesNode.type + ". node.parent: " + aPlacesNode);
- }
- return node;
- },
-
- get controller() this._controller,
-
- get selType() "single",
- selectItems: function() { },
- selectAll: function() { },
-
- get selectedNode() {
- if (this._contextMenuShown) {
- let anchor = this._contextMenuShown.triggerNode;
- if (!anchor)
- return null;
-
- if (anchor._placesNode)
- return this._rootElt == anchor ? null : anchor._placesNode;
-
- anchor = anchor.parentNode;
- return this._rootElt == anchor ? null : (anchor._placesNode || null);
- }
- return null;
- },
-
- get hasSelection() this.selectedNode != null,
-
- get selectedNodes() {
- let selectedNode = this.selectedNode;
- return selectedNode ? [selectedNode] : [];
- },
-
- get removableSelectionRanges() {
- // On static content the current selectedNode would be the selection's
- // parent node. We don't want to allow removing a node when the
- // selection is not explicit.
- if (document.popupNode &&
- (document.popupNode == "menupopup" || !document.popupNode._placesNode))
- return [];
-
- return [this.selectedNodes];
- },
-
- get draggableSelection() [this._draggedElt],
-
- get insertionPoint() {
- // There is no insertion point for history queries, so bail out now and
- // save a lot of work when updating commands.
- let resultNode = this._resultNode;
- if (PlacesUtils.nodeIsQuery(resultNode) &&
- PlacesUtils.asQuery(resultNode).queryOptions.queryType ==
- Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
- return null;
-
- // By default, the insertion point is at the top level, at the end.
- let index = PlacesUtils.bookmarks.DEFAULT_INDEX;
- let container = this._resultNode;
- let orientation = Ci.nsITreeView.DROP_BEFORE;
- let isTag = false;
-
- let selectedNode = this.selectedNode;
- if (selectedNode) {
- let popup = document.popupNode;
- if (!popup._placesNode || popup._placesNode == this._resultNode ||
- popup._placesNode.itemId == -1) {
- // If a static menuitem is selected, or if the root node is selected,
- // the insertion point is inside the folder, at the end.
- container = selectedNode;
- orientation = Ci.nsITreeView.DROP_ON;
- }
- else {
- // In all other cases the insertion point is before that node.
- container = selectedNode.parent;
- index = container.getChildIndex(selectedNode);
- isTag = PlacesUtils.nodeIsTagQuery(container);
- }
- }
-
- if (PlacesControllerDragHelper.disallowInsertion(container))
- return null;
-
- return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
- index, orientation, isTag);
- },
-
- buildContextMenu: function PVB_buildContextMenu(aPopup) {
- this._contextMenuShown = aPopup;
- window.updateCommands("places");
- return this.controller.buildContextMenu(aPopup);
- },
-
- destroyContextMenu: function PVB_destroyContextMenu(aPopup) {
- this._contextMenuShown = null;
- },
-
- _cleanPopup: function PVB_cleanPopup(aPopup, aDelay) {
- // Remove Places nodes from the popup.
- let child = aPopup._startMarker;
- while (child.nextSibling != aPopup._endMarker) {
- let sibling = child.nextSibling;
- if (sibling._placesNode && !aDelay) {
- aPopup.removeChild(sibling);
- }
- else if (sibling._placesNode && aDelay) {
- // HACK (bug 733419): the popups originating from the OS X native
- // menubar don't live-update while open, thus we don't clean it
- // until the next popupshowing, to avoid zombie menuitems.
- if (!aPopup._delayedRemovals)
- aPopup._delayedRemovals = [];
- aPopup._delayedRemovals.push(sibling);
- child = child.nextSibling;
- }
- else {
- child = child.nextSibling;
- }
- }
- },
-
- _rebuildPopup: function PVB__rebuildPopup(aPopup) {
- let resultNode = aPopup._placesNode;
- if (!resultNode.containerOpen)
- return;
-
- if (this.controller.hasCachedLivemarkInfo(resultNode)) {
- this._setEmptyPopupStatus(aPopup, false);
- aPopup._built = true;
- this._populateLivemarkPopup(aPopup);
- return;
- }
-
- this._cleanPopup(aPopup);
-
- let cc = resultNode.childCount;
- if (cc > 0) {
- this._setEmptyPopupStatus(aPopup, false);
-
- for (let i = 0; i < cc; ++i) {
- let child = resultNode.getChild(i);
- this._insertNewItemToPopup(child, aPopup, null);
- }
- }
- else {
- this._setEmptyPopupStatus(aPopup, true);
- }
- aPopup._built = true;
- },
-
- _removeChild: function PVB__removeChild(aChild) {
- // If document.popupNode pointed to this child, null it out,
- // otherwise controller's command-updating may rely on the removed
- // item still being "selected".
- if (document.popupNode == aChild)
- document.popupNode = null;
-
- aChild.parentNode.removeChild(aChild);
- },
-
- _setEmptyPopupStatus:
- function PVB__setEmptyPopupStatus(aPopup, aEmpty) {
- if (!aPopup._emptyMenuitem) {
- let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
- aPopup._emptyMenuitem = document.createElement("menuitem");
- aPopup._emptyMenuitem.setAttribute("label", label);
- aPopup._emptyMenuitem.setAttribute("disabled", true);
- }
-
- if (aEmpty) {
- aPopup.setAttribute("emptyplacesresult", "true");
- // Don't add the menuitem if there is static content.
- if (!aPopup._startMarker.previousSibling &&
- !aPopup._endMarker.nextSibling)
- aPopup.insertBefore(aPopup._emptyMenuitem, aPopup._endMarker);
- }
- else {
- aPopup.removeAttribute("emptyplacesresult");
- try {
- aPopup.removeChild(aPopup._emptyMenuitem);
- } catch (ex) {}
- }
- },
-
- _createMenuItemForPlacesNode:
- function PVB__createMenuItemForPlacesNode(aPlacesNode) {
- this._domNodes.delete(aPlacesNode);
-
- let element;
- let type = aPlacesNode.type;
- if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
- element = document.createElement("menuseparator");
- }
- else {
- let itemId = aPlacesNode.itemId;
- if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_URI) {
- element = document.createElement("menuitem");
- element.className = "menuitem-iconic bookmark-item menuitem-with-favicon";
- element.setAttribute("scheme",
- PlacesUIUtils.guessUrlSchemeForUI(aPlacesNode.uri));
- }
- else if (PlacesUtils.containerTypes.indexOf(type) != -1) {
- element = document.createElement("menu");
- element.setAttribute("container", "true");
-
- if (aPlacesNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) {
- element.setAttribute("query", "true");
- if (PlacesUtils.nodeIsTagQuery(aPlacesNode))
- element.setAttribute("tagContainer", "true");
- else if (PlacesUtils.nodeIsDay(aPlacesNode))
- element.setAttribute("dayContainer", "true");
- else if (PlacesUtils.nodeIsHost(aPlacesNode))
- element.setAttribute("hostContainer", "true");
- }
- else if (itemId != -1) {
- PlacesUtils.livemarks.getLivemark({ id: itemId })
- .then(aLivemark => {
- element.setAttribute("livemark", "true");
-#ifdef XP_MACOSX
- // OS X native menubar doesn't track list-style-images since
- // it doesn't have a frame (bug 733415). Thus enforce updating.
- element.setAttribute("image", "");
- element.removeAttribute("image");
-#endif
- this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
- }, () => undefined);
- }
-
- let popup = document.createElement("menupopup");
- popup._placesNode = PlacesUtils.asContainer(aPlacesNode);
-
- if (!this._nativeView) {
- popup.setAttribute("placespopup", "true");
- }
-
-#ifdef XP_MACOSX
- // No context menu on mac.
- popup.setAttribute("context", "placesContext");
-#endif
- element.appendChild(popup);
- element.className = "menu-iconic bookmark-item";
-
- this._domNodes.set(aPlacesNode, popup);
- }
- else
- throw "Unexpected node";
-
- element.setAttribute("label", PlacesUIUtils.getBestTitle(aPlacesNode));
-
- let icon = aPlacesNode.icon;
- if (icon)
- element.setAttribute("image",
- PlacesUIUtils.getImageURLForResolution(window, icon));
- }
-
- element._placesNode = aPlacesNode;
- if (!this._domNodes.has(aPlacesNode))
- this._domNodes.set(aPlacesNode, element);
-
- return element;
- },
-
- _insertNewItemToPopup:
- function PVB__insertNewItemToPopup(aNewChild, aPopup, aBefore) {
- let element = this._createMenuItemForPlacesNode(aNewChild);
- let before = aBefore || aPopup._endMarker;
- aPopup.insertBefore(element, before);
- return element;
- },
-
- _setLivemarkSiteURIMenuItem:
- function PVB__setLivemarkSiteURIMenuItem(aPopup) {
- let livemarkInfo = this.controller.getCachedLivemarkInfo(aPopup._placesNode);
- let siteUrl = livemarkInfo && livemarkInfo.siteURI ?
- livemarkInfo.siteURI.spec : null;
- if (!siteUrl && aPopup._siteURIMenuitem) {
- aPopup.removeChild(aPopup._siteURIMenuitem);
- aPopup._siteURIMenuitem = null;
- aPopup.removeChild(aPopup._siteURIMenuseparator);
- aPopup._siteURIMenuseparator = null;
- }
- else if (siteUrl && !aPopup._siteURIMenuitem) {
- // Add "Open (Feed Name)" menuitem.
- aPopup._siteURIMenuitem = document.createElement("menuitem");
- aPopup._siteURIMenuitem.className = "openlivemarksite-menuitem";
- aPopup._siteURIMenuitem.setAttribute("targetURI", siteUrl);
- aPopup._siteURIMenuitem.setAttribute("oncommand",
- "openUILink(this.getAttribute('targetURI'), event);");
-
- // If a user middle-clicks this item we serve the oncommand event.
- // We are using checkForMiddleClick because of Bug 246720.
- // Note: stopPropagation is needed to avoid serving middle-click
- // with BT_onClick that would open all items in tabs.
- aPopup._siteURIMenuitem.setAttribute("onclick",
- "checkForMiddleClick(this, event); event.stopPropagation();");
- let label =
- PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label",
- [aPopup.parentNode.getAttribute("label")])
- aPopup._siteURIMenuitem.setAttribute("label", label);
- aPopup.insertBefore(aPopup._siteURIMenuitem, aPopup._startMarker);
-
- aPopup._siteURIMenuseparator = document.createElement("menuseparator");
- aPopup.insertBefore(aPopup._siteURIMenuseparator, aPopup._startMarker);
- }
- },
-
- /**
- * Add, update or remove the livemark status menuitem.
- * @param aPopup
- * The livemark container popup
- * @param aStatus
- * The livemark status
- */
- _setLivemarkStatusMenuItem:
- function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) {
- let statusMenuitem = aPopup._statusMenuitem;
- if (!statusMenuitem) {
- // Create the status menuitem and cache it in the popup object.
- statusMenuitem = document.createElement("menuitem");
- statusMenuitem.className = "livemarkstatus-menuitem";
- statusMenuitem.setAttribute("disabled", true);
- aPopup._statusMenuitem = statusMenuitem;
- }
-
- if (aStatus == Ci.mozILivemark.STATUS_LOADING ||
- aStatus == Ci.mozILivemark.STATUS_FAILED) {
- // Status has changed, update the cached status menuitem.
- let stringId = aStatus == Ci.mozILivemark.STATUS_LOADING ?
- "bookmarksLivemarkLoading" : "bookmarksLivemarkFailed";
- statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
- if (aPopup._startMarker.nextSibling != statusMenuitem)
- aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
- }
- else {
- // The livemark has finished loading.
- if (aPopup._statusMenuitem.parentNode == aPopup)
- aPopup.removeChild(aPopup._statusMenuitem);
- }
- },
-
- toggleCutNode: function PVB_toggleCutNode(aPlacesNode, aValue) {
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
-
- // We may get the popup for menus, but we need the menu itself.
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
- if (aValue)
- elt.setAttribute("cutting", "true");
- else
- elt.removeAttribute("cutting");
- },
-
- nodeURIChanged: function PVB_nodeURIChanged(aPlacesNode, aURIString) {
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
-
- // Here we need the <menu>.
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
-
- elt.setAttribute("scheme", PlacesUIUtils.guessUrlSchemeForUI(aURIString));
- },
-
- nodeIconChanged: function PVB_nodeIconChanged(aPlacesNode) {
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
-
- // There's no UI representation for the root node, thus there's nothing to
- // be done when the icon changes.
- if (elt == this._rootElt)
- return;
-
- // Here we need the <menu>.
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
-
- let icon = aPlacesNode.icon;
- if (!icon)
- elt.removeAttribute("image");
- else if (icon != elt.getAttribute("image"))
- elt.setAttribute("image",
- PlacesUIUtils.getImageURLForResolution(window, icon));
- },
-
- nodeAnnotationChanged:
- function PVB_nodeAnnotationChanged(aPlacesNode, aAnno) {
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
-
- // All livemarks have a feedURI, so use it as our indicator of a livemark
- // being modified.
- if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
- let menu = elt.parentNode;
- if (!menu.hasAttribute("livemark")) {
- menu.setAttribute("livemark", "true");
-#ifdef XP_MACOSX
- // OS X native menubar doesn't track list-style-images since
- // it doesn't have a frame (bug 733415). Thus enforce updating.
- menu.setAttribute("image", "");
- menu.removeAttribute("image");
-#endif
- }
-
- PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
- .then(aLivemark => {
- // Controller will use this to build the meta data for the node.
- this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
- this.invalidateContainer(aPlacesNode);
- }, () => undefined);
- }
- },
-
- nodeTitleChanged:
- function PVB_nodeTitleChanged(aPlacesNode, aNewTitle) {
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
-
- // There's no UI representation for the root node, thus there's
- // nothing to be done when the title changes.
- if (elt == this._rootElt)
- return;
-
- // Here we need the <menu>.
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
-
- if (!aNewTitle && elt.localName != "toolbarbutton") {
- // Many users consider toolbars as shortcuts containers, so explicitly
- // allow empty labels on toolbarbuttons. For any other element try to be
- // smarter, guessing a title from the uri.
- elt.setAttribute("label", PlacesUIUtils.getBestTitle(aPlacesNode));
- }
- else {
- elt.setAttribute("label", aNewTitle);
- }
- },
-
- nodeRemoved:
- function PVB_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
- let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
-
- // Here we need the <menu>.
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
-
- if (parentElt._built) {
- parentElt.removeChild(elt);
-
- // Figure out if we need to show the "<Empty>" menu-item.
- // TODO Bug 517701: This doesn't seem to handle the case of an empty
- // root.
- if (parentElt._startMarker.nextSibling == parentElt._endMarker)
- this._setEmptyPopupStatus(parentElt, true);
- }
- },
-
- nodeHistoryDetailsChanged:
- function PVB_nodeHistoryDetailsChanged(aPlacesNode, aTime, aCount) {
- if (aPlacesNode.parent &&
- this.controller.hasCachedLivemarkInfo(aPlacesNode.parent)) {
- // Find the node in the parent.
- let popup = this._getDOMNodeForPlacesNode(aPlacesNode.parent);
- for (let child = popup._startMarker.nextSibling;
- child != popup._endMarker;
- child = child.nextSibling) {
- if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) {
- if (aCount)
- child.setAttribute("visited", "true");
- else
- child.removeAttribute("visited");
- break;
- }
- }
- }
- },
-
- nodeTagsChanged: function() { },
- nodeDateAddedChanged: function() { },
- nodeLastModifiedChanged: function() { },
- nodeKeywordChanged: function() { },
- sortingChanged: function() { },
- batching: function() { },
-
- nodeInserted:
- function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
- let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
- if (!parentElt._built)
- return;
-
- let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
- aIndex + 1;
- this._insertNewItemToPopup(aPlacesNode, parentElt,
- parentElt.childNodes[index]);
- this._setEmptyPopupStatus(parentElt, false);
- },
-
- nodeMoved:
- function PBV_nodeMoved(aPlacesNode,
- aOldParentPlacesNode, aOldIndex,
- aNewParentPlacesNode, aNewIndex) {
- // Note: the current implementation of moveItem does not actually
- // use this notification when the item in question is moved from one
- // folder to another. Instead, it calls nodeRemoved and nodeInserted
- // for the two folders. Thus, we can assume old-parent == new-parent.
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
-
- // Here we need the <menu>.
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
-
- // If our root node is a folder, it might be moved. There's nothing
- // we need to do in that case.
- if (elt == this._rootElt)
- return;
-
- let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
- if (parentElt._built) {
- // Move the node.
- parentElt.removeChild(elt);
- let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
- aNewIndex + 1;
- parentElt.insertBefore(elt, parentElt.childNodes[index]);
- }
- },
-
- containerStateChanged:
- function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) {
- if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED ||
- aNewState == Ci.nsINavHistoryContainerResultNode.STATE_CLOSED) {
- this.invalidateContainer(aPlacesNode);
-
- if (PlacesUtils.nodeIsFolder(aPlacesNode)) {
- let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
- if (queryOptions.excludeItems) {
- return;
- }
-
- PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
- .then(aLivemark => {
- let shouldInvalidate =
- !this.controller.hasCachedLivemarkInfo(aPlacesNode);
- this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
- if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) {
- aLivemark.registerForUpdates(aPlacesNode, this);
- // Prioritize the current livemark.
- aLivemark.reload();
- PlacesUtils.livemarks.reloadLivemarks();
- if (shouldInvalidate)
- this.invalidateContainer(aPlacesNode);
- }
- else {
- aLivemark.unregisterForUpdates(aPlacesNode);
- }
- }, () => undefined);
- }
- }
- },
-
- _populateLivemarkPopup: function PVB__populateLivemarkPopup(aPopup)
- {
- this._setLivemarkSiteURIMenuItem(aPopup);
- // Show the loading status only if there are no entries yet.
- if (aPopup._startMarker.nextSibling == aPopup._endMarker)
- this._setLivemarkStatusMenuItem(aPopup, Ci.mozILivemark.STATUS_LOADING);
-
- PlacesUtils.livemarks.getLivemark({ id: aPopup._placesNode.itemId })
- .then(aLivemark => {
- let placesNode = aPopup._placesNode;
- if (!placesNode.containerOpen)
- return;
-
- if (aLivemark.status != Ci.mozILivemark.STATUS_LOADING)
- this._setLivemarkStatusMenuItem(aPopup, aLivemark.status);
- this._cleanPopup(aPopup,
- this._nativeView && aPopup.parentNode.hasAttribute("open"));
-
- let children = aLivemark.getNodesForContainer(placesNode);
- for (let i = 0; i < children.length; i++) {
- let child = children[i];
- this.nodeInserted(placesNode, child, i);
- if (child.accessCount)
- this._getDOMNodeForPlacesNode(child).setAttribute("visited", true);
- else
- this._getDOMNodeForPlacesNode(child).removeAttribute("visited");
- }
- }, Components.utils.reportError);
- },
-
- invalidateContainer: function PVB_invalidateContainer(aPlacesNode) {
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
- elt._built = false;
-
- // If the menupopup is open we should live-update it.
- if (elt.parentNode.open)
- this._rebuildPopup(elt);
- },
-
- uninit: function PVB_uninit() {
- if (this._result) {
- this._result.removeObserver(this);
- this._resultNode.containerOpen = false;
- this._resultNode = null;
- this._result = null;
- }
-
- if (this._controller) {
- this._controller.terminate();
- // Removing the controller will fail if it is already no longer there.
- // This can happen if the view element was removed/reinserted without
- // our knowledge. There is no way to check for that having happened
- // without the possibility of an exception. :-(
- try {
- this._viewElt.controllers.removeController(this._controller);
- } catch (ex) {
- } finally {
- this._controller = null;
- }
- }
-
- delete this._viewElt._placesView;
- },
-
- get isRTL() {
- if ("_isRTL" in this)
- return this._isRTL;
-
- return this._isRTL = document.defaultView
- .getComputedStyle(this.viewElt, "")
- .direction == "rtl";
- },
-
- get ownerWindow() window,
-
- /**
- * Adds an "Open All in Tabs" menuitem to the bottom of the popup.
- * @param aPopup
- * a Places popup.
- */
- _mayAddCommandsItems: function PVB__mayAddCommandsItems(aPopup) {
- // The command items are never added to the root popup.
- if (aPopup == this._rootElt)
- return;
-
- let hasMultipleURIs = false;
-
- // Check if the popup contains at least 2 menuitems with places nodes.
- // We don't currently support opening multiple uri nodes when they are not
- // populated by the result.
- if (aPopup._placesNode.childCount > 0) {
- let currentChild = aPopup.firstChild;
- let numURINodes = 0;
- while (currentChild) {
- if (currentChild.localName == "menuitem" && currentChild._placesNode) {
- if (++numURINodes == 2)
- break;
- }
- currentChild = currentChild.nextSibling;
- }
- hasMultipleURIs = numURINodes > 1;
- }
-
- let isLiveMark = false;
- if (this.controller.hasCachedLivemarkInfo(aPopup._placesNode)) {
- hasMultipleURIs = true;
- isLiveMark = true;
- }
-
- if (!hasMultipleURIs) {
- // We don't have to show any option.
- if (aPopup._endOptOpenAllInTabs) {
- aPopup.removeChild(aPopup._endOptOpenAllInTabs);
- aPopup._endOptOpenAllInTabs = null;
-
- aPopup.removeChild(aPopup._endOptSeparator);
- aPopup._endOptSeparator = null;
- }
- }
- else if (!aPopup._endOptOpenAllInTabs) {
- // Create a separator before options.
- aPopup._endOptSeparator = document.createElement("menuseparator");
- aPopup._endOptSeparator.className = "bookmarks-actions-menuseparator";
- aPopup.appendChild(aPopup._endOptSeparator);
-
- // Add the "Open All in Tabs" menuitem.
- aPopup._endOptOpenAllInTabs = document.createElement("menuitem");
- aPopup._endOptOpenAllInTabs.className = "openintabs-menuitem";
- if (isLiveMark) {
- aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
- "PlacesUIUtils.openLiveMarkNodesInTabs(this.parentNode._placesNode, event, " +
- "PlacesUIUtils.getViewForNode(this));");
- } else {
- aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
- "PlacesUIUtils.openContainerNodeInTabs(this.parentNode._placesNode, event, " +
- "PlacesUIUtils.getViewForNode(this));");
- }
- aPopup._endOptOpenAllInTabs.setAttribute("onclick",
- "checkForMiddleClick(this, event); event.stopPropagation();");
- aPopup._endOptOpenAllInTabs.setAttribute("label",
- gNavigatorBundle.getString("menuOpenAllInTabs.label"));
- aPopup.appendChild(aPopup._endOptOpenAllInTabs);
- }
- },
-
- _ensureMarkers: function PVB__ensureMarkers(aPopup) {
- if (aPopup._startMarker)
- return;
-
- // _startMarker is an hidden menuseparator that lives before places nodes.
- aPopup._startMarker = document.createElement("menuseparator");
- aPopup._startMarker.hidden = true;
- aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild);
-
- // _endMarker is an hidden menuseparator that lives after places nodes.
- aPopup._endMarker = document.createElement("menuseparator");
- aPopup._endMarker.hidden = true;
- aPopup.appendChild(aPopup._endMarker);
-
- // Move the markers to the right position.
- let firstNonStaticNodeFound = false;
- for (let i = 0; i < aPopup.childNodes.length; i++) {
- let child = aPopup.childNodes[i];
- // Menus that have static content at the end, but are initially empty,
- // use a special "builder" attribute to figure out where to start
- // inserting places nodes.
- if (child.getAttribute("builder") == "end") {
- aPopup.insertBefore(aPopup._endMarker, child);
- break;
- }
-
- if (child._placesNode && !firstNonStaticNodeFound) {
- firstNonStaticNodeFound = true;
- aPopup.insertBefore(aPopup._startMarker, child);
- }
- }
- if (!firstNonStaticNodeFound) {
- aPopup.insertBefore(aPopup._startMarker, aPopup._endMarker);
- }
- },
-
- _onPopupShowing: function PVB__onPopupShowing(aEvent) {
- // Avoid handling popupshowing of inner views.
- let popup = aEvent.originalTarget;
-
- this._ensureMarkers(popup);
-
- // Remove any delayed element, see _cleanPopup for details.
- if ("_delayedRemovals" in popup) {
- while (popup._delayedRemovals.length > 0) {
- popup.removeChild(popup._delayedRemovals.shift());
- }
- }
-
- if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
- if (!popup._placesNode.containerOpen)
- popup._placesNode.containerOpen = true;
- if (!popup._built)
- this._rebuildPopup(popup);
-
- this._mayAddCommandsItems(popup);
- }
- },
-
- _addEventListeners:
- function PVB__addEventListeners(aObject, aEventNames, aCapturing) {
- for (let i = 0; i < aEventNames.length; i++) {
- aObject.addEventListener(aEventNames[i], this, aCapturing);
- }
- },
-
- _removeEventListeners:
- function PVB__removeEventListeners(aObject, aEventNames, aCapturing) {
- for (let i = 0; i < aEventNames.length; i++) {
- aObject.removeEventListener(aEventNames[i], this, aCapturing);
- }
- },
-};
-
-function PlacesToolbar(aPlace) {
- let startTime = Date.now();
- // Add some smart getters for our elements.
- let thisView = this;
- [
- ["_viewElt", "PlacesToolbar"],
- ["_rootElt", "PlacesToolbarItems"],
- ["_dropIndicator", "PlacesToolbarDropIndicator"],
- ["_chevron", "PlacesChevron"],
- ["_chevronPopup", "PlacesChevronPopup"]
- ].forEach(function (elementGlobal) {
- let [name, id] = elementGlobal;
- thisView.__defineGetter__(name, function () {
- let element = document.getElementById(id);
- if (!element)
- return null;
-
- delete thisView[name];
- return thisView[name] = element;
- });
- });
-
- this._viewElt._placesView = this;
-
- this._addEventListeners(this._viewElt, this._cbEvents, false);
- this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
- this._addEventListeners(this._rootElt, ["overflow", "underflow"], true);
- this._addEventListeners(window, ["resize", "unload"], false);
-
- // If personal-bookmarks has been dragged to the tabs toolbar,
- // we have to track addition and removals of tabs, to properly
- // recalculate the available space for bookmarks.
- // TODO (bug 734730): Use a performant mutation listener when available.
- if (this._viewElt.parentNode.parentNode == document.getElementById("TabsToolbar")) {
- this._addEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
- }
-
- PlacesViewBase.call(this, aPlace);
-}
-
-PlacesToolbar.prototype = {
- __proto__: PlacesViewBase.prototype,
-
- _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop",
- "mousemove", "mouseover", "mouseout"],
-
- QueryInterface: function PT_QueryInterface(aIID) {
- if (aIID.equals(Ci.nsIDOMEventListener) ||
- aIID.equals(Ci.nsITimerCallback))
- return this;
-
- return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
- },
-
- uninit: function PT_uninit() {
- this._removeEventListeners(this._viewElt, this._cbEvents, false);
- this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
- true);
- this._removeEventListeners(this._rootElt, ["overflow", "underflow"], true);
- this._removeEventListeners(window, ["resize", "unload"], false);
- this._removeEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
-
- PlacesViewBase.prototype.uninit.apply(this, arguments);
- },
-
- _openedMenuButton: null,
- _allowPopupShowing: true,
-
- _rebuild: function PT__rebuild() {
- // Clear out references to existing nodes, since they will be removed
- // and re-added.
- if (this._overFolder.elt)
- this._clearOverFolder();
-
- this._openedMenuButton = null;
- while (this._rootElt.hasChildNodes()) {
- this._rootElt.removeChild(this._rootElt.firstChild);
- }
-
- let cc = this._resultNode.childCount;
- for (let i = 0; i < cc; ++i) {
- this._insertNewItem(this._resultNode.getChild(i), null);
- }
-
- if (this._chevronPopup.hasAttribute("type")) {
- // Chevron has already been initialized, but since we are forcing
- // a rebuild of the toolbar, it has to be rebuilt.
- // Otherwise, it will be initialized when the toolbar overflows.
- this._chevronPopup.place = this.place;
- }
- },
-
- _insertNewItem:
- function PT__insertNewItem(aChild, aBefore) {
- this._domNodes.delete(aChild);
-
- let type = aChild.type;
- let button;
- if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
- button = document.createElement("toolbarseparator");
- }
- else {
- button = document.createElement("toolbarbutton");
- button.className = "bookmark-item";
- button.setAttribute("label", aChild.title || "");
- let icon = aChild.icon;
- if (icon)
- button.setAttribute("image",
- PlacesUIUtils.getImageURLForResolution(window, icon));
-
- if (PlacesUtils.containerTypes.indexOf(type) != -1) {
- button.setAttribute("type", "menu");
- button.setAttribute("container", "true");
-
- if (PlacesUtils.nodeIsQuery(aChild)) {
- button.setAttribute("query", "true");
- if (PlacesUtils.nodeIsTagQuery(aChild))
- button.setAttribute("tagContainer", "true");
- }
- else if (PlacesUtils.nodeIsFolder(aChild)) {
- PlacesUtils.livemarks.getLivemark({ id: aChild.itemId })
- .then(aLivemark => {
- button.setAttribute("livemark", "true");
- this.controller.cacheLivemarkInfo(aChild, aLivemark);
- }, () => undefined);
- }
-
- let popup = document.createElement("menupopup");
- popup.setAttribute("placespopup", "true");
- button.appendChild(popup);
- popup._placesNode = PlacesUtils.asContainer(aChild);
-#ifndef XP_MACOSX
- popup.setAttribute("context", "placesContext");
-#endif
-
- this._domNodes.set(aChild, popup);
- }
- else if (PlacesUtils.nodeIsURI(aChild)) {
- button.setAttribute("scheme",
- PlacesUIUtils.guessUrlSchemeForUI(aChild.uri));
- }
- }
-
- button._placesNode = aChild;
- if (!this._domNodes.has(aChild))
- this._domNodes.set(aChild, button);
-
- if (aBefore)
- this._rootElt.insertBefore(button, aBefore);
- else
- this._rootElt.appendChild(button);
- },
-
- _updateChevronPopupNodesVisibility:
- function PT__updateChevronPopupNodesVisibility() {
- for (let i = 0, node = this._chevronPopup._startMarker.nextSibling;
- node != this._chevronPopup._endMarker;
- i++, node = node.nextSibling) {
- node.hidden = this._rootElt.childNodes[i].style.visibility != "hidden";
- }
- },
-
- _onChevronPopupShowing:
- function PT__onChevronPopupShowing(aEvent) {
- // Handle popupshowing only for the chevron popup, not for nested ones.
- if (aEvent.target != this._chevronPopup)
- return;
-
- if (!this._chevron._placesView)
- this._chevron._placesView = new PlacesMenu(aEvent, this.place);
-
- this._updateChevronPopupNodesVisibility();
- },
-
- handleEvent: function PT_handleEvent(aEvent) {
- switch (aEvent.type) {
- case "unload":
- this.uninit();
- break;
- case "resize":
- // This handler updates nodes visibility in both the toolbar
- // and the chevron popup when a window resize does not change
- // the overflow status of the toolbar.
- this.updateChevron();
- break;
- case "overflow":
- if (aEvent.target != aEvent.currentTarget)
- return;
-
- // Ignore purely vertical overflows.
- if (aEvent.detail == 0)
- return;
-
- // Attach the popup binding to the chevron popup if it has not yet
- // been initialized.
- if (!this._chevronPopup.hasAttribute("type")) {
- this._chevronPopup.setAttribute("place", this.place);
- this._chevronPopup.setAttribute("type", "places");
- }
- this._chevron.collapsed = false;
- this.updateChevron();
- break;
- case "underflow":
- if (aEvent.target != aEvent.currentTarget)
- return;
-
- // Ignore purely vertical underflows.
- if (aEvent.detail == 0)
- return;
-
- this.updateChevron();
- this._chevron.collapsed = true;
- break;
- case "TabOpen":
- case "TabClose":
- this.updateChevron();
- break;
- case "dragstart":
- this._onDragStart(aEvent);
- break;
- case "dragover":
- this._onDragOver(aEvent);
- break;
- case "dragexit":
- this._onDragExit(aEvent);
- break;
- case "dragend":
- this._onDragEnd(aEvent);
- break;
- case "drop":
- this._onDrop(aEvent);
- break;
- case "mouseover":
- this._onMouseOver(aEvent);
- break;
- case "mousemove":
- this._onMouseMove(aEvent);
- break;
- case "mouseout":
- this._onMouseOut(aEvent);
- break;
- case "popupshowing":
- this._onPopupShowing(aEvent);
- break;
- case "popuphidden":
- this._onPopupHidden(aEvent);
- break;
- default:
- throw "Trying to handle unexpected event.";
- }
- },
-
- updateChevron: function PT_updateChevron() {
- // If the chevron is collapsed there's nothing to update.
- if (this._chevron.collapsed)
- return;
-
- // Update the chevron on a timer. This will avoid repeated work when
- // lot of changes happen in a small timeframe.
- if (this._updateChevronTimer)
- this._updateChevronTimer.cancel();
-
- this._updateChevronTimer = this._setTimer(100);
- },
-
- _updateChevronTimerCallback: function PT__updateChevronTimerCallback() {
- let scrollRect = this._rootElt.getBoundingClientRect();
- let childOverflowed = false;
- for (let i = 0; i < this._rootElt.childNodes.length; i++) {
- let child = this._rootElt.childNodes[i];
- // Once a child overflows, all the next ones will.
- if (!childOverflowed) {
- let childRect = child.getBoundingClientRect();
- childOverflowed = this.isRTL ? (childRect.left < scrollRect.left)
- : (childRect.right > scrollRect.right);
-
- }
- child.style.visibility = childOverflowed ? "hidden" : "visible";
- }
-
- // We rebuild the chevron on popupShowing, so if it is open
- // we must update it.
- if (this._chevron.open)
- this._updateChevronPopupNodesVisibility();
- },
-
- nodeInserted:
- function PT_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
- let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
- if (parentElt == this._rootElt) {
- let children = this._rootElt.childNodes;
- this._insertNewItem(aPlacesNode,
- aIndex < children.length ? children[aIndex] : null);
- this.updateChevron();
- return;
- }
-
- PlacesViewBase.prototype.nodeInserted.apply(this, arguments);
- },
-
- nodeRemoved:
- function PT_nodeRemoved(aParentPlacesNode, aPlacesNode, aIndex) {
- let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
-
- // Here we need the <menu>.
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
-
- if (parentElt == this._rootElt) {
- this._removeChild(elt);
- this.updateChevron();
- return;
- }
-
- PlacesViewBase.prototype.nodeRemoved.apply(this, arguments);
- },
-
- nodeMoved:
- function PT_nodeMoved(aPlacesNode,
- aOldParentPlacesNode, aOldIndex,
- aNewParentPlacesNode, aNewIndex) {
- let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
- if (parentElt == this._rootElt) {
- // Container is on the toolbar.
-
- // Move the element.
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
-
- // Here we need the <menu>.
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
-
- this._removeChild(elt);
- this._rootElt.insertBefore(elt, this._rootElt.childNodes[aNewIndex]);
-
- // The chevron view may get nodeMoved after the toolbar. In such a case,
- // we should ensure (by manually swapping menuitems) that the actual nodes
- // are in the final position before updateChevron tries to updates their
- // visibility, or the chevron may go out of sync.
- // Luckily updateChevron runs on a timer, so, by the time it updates
- // nodes, the menu has already handled the notification.
-
- this.updateChevron();
- return;
- }
-
- PlacesViewBase.prototype.nodeMoved.apply(this, arguments);
- },
-
- nodeAnnotationChanged:
- function PT_nodeAnnotationChanged(aPlacesNode, aAnno) {
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
- if (elt == this._rootElt)
- return;
-
- // We're notified for the menupopup, not the containing toolbarbutton.
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
-
- if (elt.parentNode == this._rootElt) {
- // Node is on the toolbar.
-
- // All livemarks have a feedURI, so use it as our indicator.
- if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
- elt.setAttribute("livemark", true);
-
- PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId })
- .then(aLivemark => {
- this.controller.cacheLivemarkInfo(aPlacesNode, aLivemark);
- this.invalidateContainer(aPlacesNode);
- }, Components.utils.reportError);
- }
- }
- else {
- // Node is in a submenu.
- PlacesViewBase.prototype.nodeAnnotationChanged.apply(this, arguments);
- }
- },
-
- nodeTitleChanged: function PT_nodeTitleChanged(aPlacesNode, aNewTitle) {
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
-
- // There's no UI representation for the root node, thus there's
- // nothing to be done when the title changes.
- if (elt == this._rootElt)
- return;
-
- PlacesViewBase.prototype.nodeTitleChanged.apply(this, arguments);
-
- // Here we need the <menu>.
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
-
- if (elt.parentNode == this._rootElt) {
- // Node is on the toolbar
- this.updateChevron();
- }
- },
-
- invalidateContainer: function PT_invalidateContainer(aPlacesNode) {
- let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
- if (elt == this._rootElt) {
- // Container is the toolbar itself.
- this._rebuild();
- return;
- }
-
- PlacesViewBase.prototype.invalidateContainer.apply(this, arguments);
- },
-
- _overFolder: { elt: null,
- openTimer: null,
- hoverTime: 350,
- closeTimer: null },
-
- _clearOverFolder: function PT__clearOverFolder() {
- // The mouse is no longer dragging over the stored menubutton.
- // Close the menubutton, clear out drag styles, and clear all
- // timers for opening/closing it.
- if (this._overFolder.elt && this._overFolder.elt.lastChild) {
- if (!this._overFolder.elt.lastChild.hasAttribute("dragover")) {
- this._overFolder.elt.lastChild.hidePopup();
- }
- this._overFolder.elt.removeAttribute("dragover");
- this._overFolder.elt = null;
- }
- if (this._overFolder.openTimer) {
- this._overFolder.openTimer.cancel();
- this._overFolder.openTimer = null;
- }
- if (this._overFolder.closeTimer) {
- this._overFolder.closeTimer.cancel();
- this._overFolder.closeTimer = null;
- }
- },
-
- /**
- * This function returns information about where to drop when dragging over
- * the toolbar. The returned object has the following properties:
- * - ip: the insertion point for the bookmarks service.
- * - beforeIndex: child index to drop before, for the drop indicator.
- * - folderElt: the folder to drop into, if applicable.
- */
- _getDropPoint: function PT__getDropPoint(aEvent) {
- let result = this.result;
- if (!PlacesUtils.nodeIsFolder(this._resultNode))
- return null;
-
- let dropPoint = { ip: null, beforeIndex: null, folderElt: null };
- let elt = aEvent.target;
- if (elt._placesNode && elt != this._rootElt &&
- elt.localName != "menupopup") {
- let eltRect = elt.getBoundingClientRect();
- let eltIndex = Array.indexOf(this._rootElt.childNodes, elt);
- if (PlacesUtils.nodeIsFolder(elt._placesNode) &&
- !PlacesUIUtils.isContentsReadOnly(elt._placesNode)) {
- // This is a folder.
- // If we are in the middle of it, drop inside it.
- // Otherwise, drop before it, with regards to RTL mode.
- let threshold = eltRect.width * 0.25;
- if (this.isRTL ? (aEvent.clientX > eltRect.right - threshold)
- : (aEvent.clientX < eltRect.left + threshold)) {
- // Drop before this folder.
- dropPoint.ip =
- new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
- eltIndex, Ci.nsITreeView.DROP_BEFORE);
- dropPoint.beforeIndex = eltIndex;
- }
- else if (this.isRTL ? (aEvent.clientX > eltRect.left + threshold)
- : (aEvent.clientX < eltRect.right - threshold)) {
- // Drop inside this folder.
- dropPoint.ip =
- new InsertionPoint(PlacesUtils.getConcreteItemId(elt._placesNode),
- -1, Ci.nsITreeView.DROP_ON,
- PlacesUtils.nodeIsTagQuery(elt._placesNode));
- dropPoint.beforeIndex = eltIndex;
- dropPoint.folderElt = elt;
- }
- else {
- // Drop after this folder.
- let beforeIndex =
- (eltIndex == this._rootElt.childNodes.length - 1) ?
- -1 : eltIndex + 1;
-
- dropPoint.ip =
- new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
- beforeIndex, Ci.nsITreeView.DROP_BEFORE);
- dropPoint.beforeIndex = beforeIndex;
- }
- }
- else {
- // This is a non-folder node or a read-only folder.
- // Drop before it with regards to RTL mode.
- let threshold = eltRect.width * 0.5;
- if (this.isRTL ? (aEvent.clientX > eltRect.left + threshold)
- : (aEvent.clientX < eltRect.left + threshold)) {
- // Drop before this bookmark.
- dropPoint.ip =
- new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
- eltIndex, Ci.nsITreeView.DROP_BEFORE);
- dropPoint.beforeIndex = eltIndex;
- }
- else {
- // Drop after this bookmark.
- let beforeIndex =
- eltIndex == this._rootElt.childNodes.length - 1 ?
- -1 : eltIndex + 1;
- dropPoint.ip =
- new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
- beforeIndex, Ci.nsITreeView.DROP_BEFORE);
- dropPoint.beforeIndex = beforeIndex;
- }
- }
- }
- else {
- // We are most likely dragging on the empty area of the
- // toolbar, we should drop after the last node.
- dropPoint.ip =
- new InsertionPoint(PlacesUtils.getConcreteItemId(this._resultNode),
- -1, Ci.nsITreeView.DROP_BEFORE);
- dropPoint.beforeIndex = -1;
- }
-
- return dropPoint;
- },
-
- _setTimer: function PT_setTimer(aTime) {
- let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT);
- return timer;
- },
-
- notify: function PT_notify(aTimer) {
- if (aTimer == this._updateChevronTimer) {
- this._updateChevronTimer = null;
- this._updateChevronTimerCallback();
- }
-
- // * Timer to turn off indicator bar.
- else if (aTimer == this._ibTimer) {
- this._dropIndicator.collapsed = true;
- this._ibTimer = null;
- }
-
- // * Timer to open a menubutton that's being dragged over.
- else if (aTimer == this._overFolder.openTimer) {
- // Set the autoopen attribute on the folder's menupopup so that
- // the menu will automatically close when the mouse drags off of it.
- this._overFolder.elt.lastChild.setAttribute("autoopened", "true");
- this._overFolder.elt.open = true;
- this._overFolder.openTimer = null;
- }
-
- // * Timer to close a menubutton that's been dragged off of.
- else if (aTimer == this._overFolder.closeTimer) {
- // Close the menubutton if we are not dragging over it or one of
- // its children. The autoopened attribute will let the menu know to
- // close later if the menu is still being dragged over.
- let currentPlacesNode = PlacesControllerDragHelper.currentDropTarget;
- let inHierarchy = false;
- while (currentPlacesNode) {
- if (currentPlacesNode == this._rootElt) {
- inHierarchy = true;
- break;
- }
- currentPlacesNode = currentPlacesNode.parentNode;
- }
- // The _clearOverFolder() function will close the menu for
- // _overFolder.elt. So null it out if we don't want to close it.
- if (inHierarchy)
- this._overFolder.elt = null;
-
- // Clear out the folder and all associated timers.
- this._clearOverFolder();
- }
- },
-
- _onMouseOver: function PT__onMouseOver(aEvent) {
- let button = aEvent.target;
- if (button.parentNode == this._rootElt && button._placesNode &&
- PlacesUtils.nodeIsURI(button._placesNode))
- window.XULBrowserWindow.setOverLink(aEvent.target._placesNode.uri, null);
- },
-
- _onMouseOut: function PT__onMouseOut(aEvent) {
- window.XULBrowserWindow.setOverLink("", null);
- },
-
- _cleanupDragDetails: function PT__cleanupDragDetails() {
- // Called on dragend and drop.
- PlacesControllerDragHelper.currentDropTarget = null;
- this._draggedElt = null;
- if (this._ibTimer)
- this._ibTimer.cancel();
-
- this._dropIndicator.collapsed = true;
- },
-
- _onDragStart: function PT__onDragStart(aEvent) {
- // Sub menus have their own d&d handlers.
- let draggedElt = aEvent.target;
- if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode)
- return;
-
- if (draggedElt.localName == "toolbarbutton" &&
- draggedElt.getAttribute("type") == "menu") {
- // If the drag gesture on a container is toward down we open instead
- // of dragging.
- let translateY = this._cachedMouseMoveEvent.clientY - aEvent.clientY;
- let translateX = this._cachedMouseMoveEvent.clientX - aEvent.clientX;
- if ((translateY) >= Math.abs(translateX/2)) {
- // Don't start the drag.
- aEvent.preventDefault();
- // Open the menu.
- draggedElt.open = true;
- return;
- }
-
- // If the menu is open, close it.
- if (draggedElt.open) {
- draggedElt.lastChild.hidePopup();
- draggedElt.open = false;
- }
- }
-
- // Activate the view and cache the dragged element.
- this._draggedElt = draggedElt._placesNode;
- this._rootElt.focus();
-
- this._controller.setDataTransfer(aEvent);
- aEvent.stopPropagation();
- },
-
- _onDragOver: function PT__onDragOver(aEvent) {
- // Cache the dataTransfer
- PlacesControllerDragHelper.currentDropTarget = aEvent.target;
- let dt = aEvent.dataTransfer;
-
- let dropPoint = this._getDropPoint(aEvent);
- if (!dropPoint || !dropPoint.ip ||
- !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
- this._dropIndicator.collapsed = true;
- aEvent.stopPropagation();
- return;
- }
-
- if (this._ibTimer) {
- this._ibTimer.cancel();
- this._ibTimer = null;
- }
-
- if (dropPoint.folderElt || aEvent.originalTarget == this._chevron) {
- // Dropping over a menubutton or chevron button.
- // Set styles and timer to open relative menupopup.
- let overElt = dropPoint.folderElt || this._chevron;
- if (this._overFolder.elt != overElt) {
- this._clearOverFolder();
- this._overFolder.elt = overElt;
- this._overFolder.openTimer = this._setTimer(this._overFolder.hoverTime);
- }
- if (!this._overFolder.elt.hasAttribute("dragover"))
- this._overFolder.elt.setAttribute("dragover", "true");
-
- this._dropIndicator.collapsed = true;
- }
- else {
- // Dragging over a normal toolbarbutton,
- // show indicator bar and move it to the appropriate drop point.
- let ind = this._dropIndicator;
- let halfInd = ind.clientWidth / 2;
- let translateX;
- if (this.isRTL) {
- halfInd = Math.ceil(halfInd);
- translateX = 0 - this._rootElt.getBoundingClientRect().right - halfInd;
- if (this._rootElt.firstChild) {
- if (dropPoint.beforeIndex == -1)
- translateX += this._rootElt.lastChild.getBoundingClientRect().left;
- else {
- translateX += this._rootElt.childNodes[dropPoint.beforeIndex]
- .getBoundingClientRect().right;
- }
- }
- }
- else {
- halfInd = Math.floor(halfInd);
- translateX = 0 - this._rootElt.getBoundingClientRect().left +
- halfInd;
- if (this._rootElt.firstChild) {
- if (dropPoint.beforeIndex == -1)
- translateX += this._rootElt.lastChild.getBoundingClientRect().right;
- else {
- translateX += this._rootElt.childNodes[dropPoint.beforeIndex]
- .getBoundingClientRect().left;
- }
- }
- }
-
- ind.style.transform = "translate(" + Math.round(translateX) + "px)";
- ind.style.MozMarginStart = (-ind.clientWidth) + "px";
- ind.collapsed = false;
-
- // Clear out old folder information.
- this._clearOverFolder();
- }
-
- aEvent.preventDefault();
- aEvent.stopPropagation();
- },
-
- _onDrop: function PT__onDrop(aEvent) {
- PlacesControllerDragHelper.currentDropTarget = aEvent.target;
-
- let dropPoint = this._getDropPoint(aEvent);
- if (dropPoint && dropPoint.ip) {
- PlacesControllerDragHelper.onDrop(dropPoint.ip, aEvent.dataTransfer)
- aEvent.preventDefault();
- }
-
- this._cleanupDragDetails();
- aEvent.stopPropagation();
- },
-
- _onDragExit: function PT__onDragExit(aEvent) {
- PlacesControllerDragHelper.currentDropTarget = null;
-
- // Set timer to turn off indicator bar (if we turn it off
- // here, dragenter might be called immediately after, creating
- // flicker).
- if (this._ibTimer)
- this._ibTimer.cancel();
- this._ibTimer = this._setTimer(10);
-
- // If we hovered over a folder, close it now.
- if (this._overFolder.elt)
- this._overFolder.closeTimer = this._setTimer(this._overFolder.hoverTime);
- },
-
- _onDragEnd: function PT_onDragEnd(aEvent) {
- this._cleanupDragDetails();
- },
-
- _onPopupShowing: function PT__onPopupShowing(aEvent) {
- if (!this._allowPopupShowing) {
- this._allowPopupShowing = true;
- aEvent.preventDefault();
- return;
- }
-
- let parent = aEvent.target.parentNode;
- if (parent.localName == "toolbarbutton")
- this._openedMenuButton = parent;
-
- PlacesViewBase.prototype._onPopupShowing.apply(this, arguments);
- },
-
- _onPopupHidden: function PT__onPopupHidden(aEvent) {
- let popup = aEvent.target;
- let placesNode = popup._placesNode;
- // Avoid handling popuphidden of inner views
- if (placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
- // UI performance: folder queries are cheap, keep the resultnode open
- // so we don't rebuild its contents whenever the popup is reopened.
- // Though, we want to always close feed containers so their expiration
- // status will be checked at next opening.
- if (!PlacesUtils.nodeIsFolder(placesNode) ||
- this.controller.hasCachedLivemarkInfo(placesNode)) {
- placesNode.containerOpen = false;
- }
- }
-
- let parent = popup.parentNode;
- if (parent.localName == "toolbarbutton") {
- this._openedMenuButton = null;
- // Clear the dragover attribute if present, if we are dragging into a
- // folder in the hierachy of current opened popup we don't clear
- // this attribute on clearOverFolder. See Notify for closeTimer.
- if (parent.hasAttribute("dragover"))
- parent.removeAttribute("dragover");
- }
- },
-
- _onMouseMove: function PT__onMouseMove(aEvent) {
- // Used in dragStart to prevent dragging folders when dragging down.
- this._cachedMouseMoveEvent = aEvent;
-
- if (this._openedMenuButton == null ||
- PlacesControllerDragHelper.getSession())
- return;
-
- let target = aEvent.originalTarget;
- if (this._openedMenuButton != target &&
- target.localName == "toolbarbutton" &&
- target.type == "menu") {
- this._openedMenuButton.open = false;
- target.open = true;
- }
- }
-};
-
-/**
- * View for Places menus. This object should be created during the first
- * popupshowing that's dispatched on the menu.
- */
-function PlacesMenu(aPopupShowingEvent, aPlace) {
- this._rootElt = aPopupShowingEvent.target; // <menupopup>
- this._viewElt = this._rootElt.parentNode; // <menu>
- this._viewElt._placesView = this;
- this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
- this._addEventListeners(window, ["unload"], false);
-
-#ifdef XP_MACOSX
- // Must walk up to support views in sub-menus, like Bookmarks Toolbar menu.
- for (let elt = this._viewElt.parentNode; elt; elt = elt.parentNode) {
- if (elt.localName == "menubar") {
- this._nativeView = true;
- break;
- }
- }
-#endif
-
- PlacesViewBase.call(this, aPlace);
- this._onPopupShowing(aPopupShowingEvent);
-}
-
-PlacesMenu.prototype = {
- __proto__: PlacesViewBase.prototype,
-
- QueryInterface: function PM_QueryInterface(aIID) {
- if (aIID.equals(Ci.nsIDOMEventListener))
- return this;
-
- return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
- },
-
- _removeChild: function PM_removeChild(aChild) {
- PlacesViewBase.prototype._removeChild.apply(this, arguments);
- },
-
- uninit: function PM_uninit() {
- this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
- true);
- this._removeEventListeners(window, ["unload"], false);
-
- PlacesViewBase.prototype.uninit.apply(this, arguments);
- },
-
- handleEvent: function PM_handleEvent(aEvent) {
- switch (aEvent.type) {
- case "unload":
- this.uninit();
- break;
- case "popupshowing":
- this._onPopupShowing(aEvent);
- break;
- case "popuphidden":
- this._onPopupHidden(aEvent);
- break;
- }
- },
-
- _onPopupHidden: function PM__onPopupHidden(aEvent) {
- // Avoid handling popuphidden of inner views.
- let popup = aEvent.originalTarget;
- let placesNode = popup._placesNode;
- if (!placesNode || PlacesUIUtils.getViewForNode(popup) != this)
- return;
-
- // UI performance: folder queries are cheap, keep the resultnode open
- // so we don't rebuild its contents whenever the popup is reopened.
- // Though, we want to always close feed containers so their expiration
- // status will be checked at next opening.
- if (!PlacesUtils.nodeIsFolder(placesNode) ||
- this.controller.hasCachedLivemarkInfo(placesNode))
- placesNode.containerOpen = false;
-
- // The autoopened attribute is set for folders which have been
- // automatically opened when dragged over. Turn off this attribute
- // when the folder closes because it is no longer applicable.
- popup.removeAttribute("autoopened");
- popup.removeAttribute("dragstart");
- }
-};
-
diff --git a/components/places/content/controller.js b/components/places/content/controller.js
deleted file mode 100644
index f4e272e..0000000
--- a/components/places/content/controller.js
+++ /dev/null
@@ -1,1895 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/ForgetAboutSite.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
- "resource://gre/modules/NetUtil.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
- "resource://gre/modules/PrivateBrowsingUtils.jsm");
-
-// XXXmano: we should move most/all of these constants to PlacesUtils
-const ORGANIZER_ROOT_BOOKMARKS = "place:folder=BOOKMARKS_MENU&excludeItems=1&queryType=1";
-
-// No change to the view, preserve current selection
-const RELOAD_ACTION_NOTHING = 0;
-// Inserting items new to the view, select the inserted rows
-const RELOAD_ACTION_INSERT = 1;
-// Removing items from the view, select the first item after the last selected
-const RELOAD_ACTION_REMOVE = 2;
-// Moving items within a view, don't treat the dropped items as additional
-// rows.
-const RELOAD_ACTION_MOVE = 3;
-
-// When removing a bunch of pages we split them in chunks to give some breath
-// to the main-thread.
-const REMOVE_PAGES_CHUNKLEN = 300;
-
-/**
- * Represents an insertion point within a container where we can insert
- * items.
- * @param aItemId
- * The identifier of the parent container
- * @param aIndex
- * The index within the container where we should insert
- * @param aOrientation
- * The orientation of the insertion. NOTE: the adjustments to the
- * insertion point to accommodate the orientation should be done by
- * the person who constructs the IP, not the user. The orientation
- * is provided for informational purposes only!
- * @param [optional] aIsTag
- * Indicates if parent container is a tag
- * @param [optional] aDropNearItemId
- * When defined we will calculate index based on this itemId
- * @constructor
- */
-function InsertionPoint(aItemId, aIndex, aOrientation, aIsTag,
- aDropNearItemId) {
- this.itemId = aItemId;
- this._index = aIndex;
- this.orientation = aOrientation;
- this.isTag = aIsTag;
- this.dropNearItemId = aDropNearItemId;
-}
-
-InsertionPoint.prototype = {
- set index(val) {
- return this._index = val;
- },
-
- get index() {
- if (this.dropNearItemId > 0) {
- // If dropNearItemId is set up we must calculate the real index of
- // the item near which we will drop.
- var index = PlacesUtils.bookmarks.getItemIndex(this.dropNearItemId);
- return this.orientation == Ci.nsITreeView.DROP_BEFORE ? index : index + 1;
- }
- return this._index;
- }
-};
-
-/**
- * Places Controller
- */
-
-function PlacesController(aView) {
- this._view = aView;
- XPCOMUtils.defineLazyServiceGetter(this, "clipboard",
- "@mozilla.org/widget/clipboard;1",
- "nsIClipboard");
- XPCOMUtils.defineLazyGetter(this, "profileName", function () {
- return Services.dirsvc.get("ProfD", Ci.nsIFile).leafName;
- });
-
- this._cachedLivemarkInfoObjects = new Map();
-}
-
-PlacesController.prototype = {
- /**
- * The places view.
- */
- _view: null,
-
- QueryInterface: XPCOMUtils.generateQI([
- Ci.nsIClipboardOwner
- ]),
-
- // nsIClipboardOwner
- LosingOwnership: function PC_LosingOwnership (aXferable) {
- this.cutNodes = [];
- },
-
- terminate: function PC_terminate() {
- this._releaseClipboardOwnership();
- },
-
- supportsCommand: function PC_supportsCommand(aCommand) {
- // Non-Places specific commands that we also support
- switch (aCommand) {
- case "cmd_undo":
- case "cmd_redo":
- case "cmd_cut":
- case "cmd_copy":
- case "cmd_paste":
- case "cmd_delete":
- case "cmd_selectAll":
- return true;
- }
-
- // All other Places Commands are prefixed with "placesCmd_" ... this
- // filters out other commands that we do _not_ support (see 329587).
- const CMD_PREFIX = "placesCmd_";
- return (aCommand.substr(0, CMD_PREFIX.length) == CMD_PREFIX);
- },
-
- isCommandEnabled: function PC_isCommandEnabled(aCommand) {
- switch (aCommand) {
- case "cmd_undo":
- return PlacesUtils.transactionManager.numberOfUndoItems > 0;
- case "cmd_redo":
- return PlacesUtils.transactionManager.numberOfRedoItems > 0;
- case "cmd_cut":
- case "placesCmd_cut":
- case "placesCmd_moveBookmarks":
- for (let node of this._view.selectedNodes) {
- // If selection includes history nodes or tags-as-bookmark, disallow
- // cutting.
- if (node.itemId == -1 ||
- (node.parent && PlacesUtils.nodeIsTagQuery(node.parent))) {
- return false;
- }
- }
- // Otherwise fall through to the cmd_delete check.
- case "cmd_delete":
- case "placesCmd_delete":
- case "placesCmd_deleteDataHost":
- return this._hasRemovableSelection();
- case "cmd_copy":
- case "placesCmd_copy":
- return this._view.hasSelection;
- case "cmd_paste":
- case "placesCmd_paste":
- return this._canInsert(true) && this._isClipboardDataPasteable();
- case "cmd_selectAll":
- if (this._view.selType != "single") {
- let rootNode = this._view.result.root;
- if (rootNode.containerOpen && rootNode.childCount > 0)
- return true;
- }
- return false;
- case "placesCmd_open":
- case "placesCmd_open:window":
- case "placesCmd_open:privatewindow":
- case "placesCmd_open:tab":
- var selectedNode = this._view.selectedNode;
- return selectedNode && PlacesUtils.nodeIsURI(selectedNode);
- case "placesCmd_new:folder":
- case "placesCmd_new:livemark":
- return this._canInsert();
- case "placesCmd_new:bookmark":
- return this._canInsert();
- case "placesCmd_new:separator":
- return this._canInsert() &&
- !PlacesUtils.asQuery(this._view.result.root).queryOptions.excludeItems &&
- this._view.result.sortingMode ==
- Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
- case "placesCmd_show:info":
- var selectedNode = this._view.selectedNode;
- return selectedNode && PlacesUtils.getConcreteItemId(selectedNode) != -1
- case "placesCmd_reload":
- // Livemark containers
- var selectedNode = this._view.selectedNode;
- return selectedNode && this.hasCachedLivemarkInfo(selectedNode);
- case "placesCmd_sortBy:name":
- var selectedNode = this._view.selectedNode;
- return selectedNode &&
- PlacesUtils.nodeIsFolder(selectedNode) &&
- !PlacesUIUtils.isContentsReadOnly(selectedNode) &&
- this._view.result.sortingMode ==
- Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
- case "placesCmd_createBookmark":
- var node = this._view.selectedNode;
- return node && PlacesUtils.nodeIsURI(node) && node.itemId == -1;
- case "placesCmd_openParentFolder":
- return true;
- default:
- return false;
- }
- },
-
- doCommand: function PC_doCommand(aCommand) {
- switch (aCommand) {
- case "cmd_undo":
- PlacesUtils.transactionManager.undoTransaction();
- break;
- case "cmd_redo":
- PlacesUtils.transactionManager.redoTransaction();
- break;
- case "cmd_cut":
- case "placesCmd_cut":
- this.cut();
- break;
- case "cmd_copy":
- case "placesCmd_copy":
- this.copy();
- break;
- case "cmd_paste":
- case "placesCmd_paste":
- this.paste();
- break;
- case "cmd_delete":
- case "placesCmd_delete":
- this.remove("Remove Selection");
- break;
- case "placesCmd_deleteDataHost":
- var host;
- if (PlacesUtils.nodeIsHost(this._view.selectedNode)) {
- var queries = this._view.selectedNode.getQueries();
- host = queries[0].domain;
- }
- else
- host = NetUtil.newURI(this._view.selectedNode.uri).host;
- ForgetAboutSite.removeDataFromDomain(host)
- .catch(Components.utils.reportError);
- break;
- case "cmd_selectAll":
- this.selectAll();
- break;
- case "placesCmd_open":
- PlacesUIUtils.openNodeIn(this._view.selectedNode, "current", this._view);
- break;
- case "placesCmd_open:window":
- PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view);
- break;
- case "placesCmd_open:privatewindow":
- PlacesUIUtils.openNodeIn(this._view.selectedNode, "window", this._view, true);
- break;
- case "placesCmd_open:tab":
- PlacesUIUtils.openNodeIn(this._view.selectedNode, "tab", this._view);
- break;
- case "placesCmd_new:folder":
- this.newItem("folder");
- break;
- case "placesCmd_new:bookmark":
- this.newItem("bookmark");
- break;
- case "placesCmd_new:livemark":
- this.newItem("livemark");
- break;
- case "placesCmd_new:separator":
- this.newSeparator();
- break;
- case "placesCmd_show:info":
- this.showBookmarkPropertiesForSelection();
- break;
- case "placesCmd_moveBookmarks":
- this.moveSelectedBookmarks();
- break;
- case "placesCmd_reload":
- this.reloadSelectedLivemark();
- break;
- case "placesCmd_sortBy:name":
- this.sortFolderByName();
- break;
- case "placesCmd_createBookmark":
- let node = this._view.selectedNode;
- PlacesUIUtils.showBookmarkDialog({ action: "add"
- , type: "bookmark"
- , hiddenRows: [ "description"
- , "keyword"
- , "location"
- , "loadInSidebar" ]
- , uri: NetUtil.newURI(node.uri)
- , title: node.title
- }, window.top);
- break;
- case "placesCmd_openParentFolder":
- this.openParentFolder();
- break;
- }
- },
-
- onEvent: function PC_onEvent(eventName) { },
-
-
- /**
- * Determine whether or not the selection can be removed, either by the
- * delete or cut operations based on whether or not any of its contents
- * are non-removable. We don't need to worry about recursion here since it
- * is a policy decision that a removable item not be placed inside a non-
- * removable item.
- * @returns true if all nodes in the selection can be removed,
- * false otherwise.
- */
- _hasRemovableSelection() {
- var ranges = this._view.removableSelectionRanges;
- if (!ranges.length)
- return false;
-
- var root = this._view.result.root;
-
- for (var j = 0; j < ranges.length; j++) {
- var nodes = ranges[j];
- for (var i = 0; i < nodes.length; ++i) {
- // Disallow removing the view's root node
- if (nodes[i] == root)
- return false;
-
- if (!PlacesUIUtils.canUserRemove(nodes[i]))
- return false;
- }
- }
-
- return true;
- },
-
- /**
- * Determines whether or not nodes can be inserted relative to the selection.
- */
- _canInsert: function PC__canInsert(isPaste) {
- var ip = this._view.insertionPoint;
- return ip != null && (isPaste || ip.isTag != true);
- },
-
- /**
- * Looks at the data on the clipboard to see if it is paste-able.
- * Paste-able data is:
- * - in a format that the view can receive
- * @returns true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor,
- - clipboard data is of type TEXT_UNICODE and
- is a valid URI.
- */
- _isClipboardDataPasteable: function PC__isClipboardDataPasteable() {
- // if the clipboard contains TYPE_X_MOZ_PLACE_* data, it is definitely
- // pasteable, with no need to unwrap all the nodes.
-
- var flavors = PlacesControllerDragHelper.placesFlavors;
- var clipboard = this.clipboard;
- var hasPlacesData =
- clipboard.hasDataMatchingFlavors(flavors, flavors.length,
- Ci.nsIClipboard.kGlobalClipboard);
- if (hasPlacesData)
- return this._view.insertionPoint != null;
-
- // if the clipboard doesn't have TYPE_X_MOZ_PLACE_* data, we also allow
- // pasting of valid "text/unicode" and "text/x-moz-url" data
- var xferable = Cc["@mozilla.org/widget/transferable;1"].
- createInstance(Ci.nsITransferable);
- xferable.init(null);
-
- xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_URL);
- xferable.addDataFlavor(PlacesUtils.TYPE_UNICODE);
- clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
-
- try {
- // getAnyTransferData will throw if no data is available.
- var data = { }, type = { };
- xferable.getAnyTransferData(type, data, { });
- data = data.value.QueryInterface(Ci.nsISupportsString).data;
- if (type.value != PlacesUtils.TYPE_X_MOZ_URL &&
- type.value != PlacesUtils.TYPE_UNICODE)
- return false;
-
- // unwrapNodes() will throw if the data blob is malformed.
- var unwrappedNodes = PlacesUtils.unwrapNodes(data, type.value);
- return this._view.insertionPoint != null;
- }
- catch (e) {
- // getAnyTransferData or unwrapNodes failed
- return false;
- }
- },
-
- /**
- * Gathers information about the selected nodes according to the following
- * rules:
- * "link" node is a URI
- * "bookmark" node is a bookmark
- * "livemarkChild" node is a child of a livemark
- * "tagChild" node is a child of a tag
- * "folder" node is a folder
- * "query" node is a query
- * "separator" node is a separator line
- * "host" node is a host
- *
- * @returns an array of objects corresponding the selected nodes. Each
- * object has each of the properties above set if its corresponding
- * node matches the rule. In addition, the annotations names for each
- * node are set on its corresponding object as properties.
- * Notes:
- * 1) This can be slow, so don't call it anywhere performance critical!
- */
- _buildSelectionMetadata: function PC__buildSelectionMetadata() {
- var metadata = [];
- var nodes = this._view.selectedNodes;
-
- for (var i = 0; i < nodes.length; i++) {
- var nodeData = {};
- var node = nodes[i];
- var nodeType = node.type;
- var uri = null;
-
- // We don't use the nodeIs* methods here to avoid going through the type
- // property way too often
- switch (nodeType) {
- case Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY:
- nodeData["query"] = true;
- if (node.parent) {
- switch (PlacesUtils.asQuery(node.parent).queryOptions.resultType) {
- case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
- nodeData["host"] = true;
- break;
- case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
- case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY:
- nodeData["day"] = true;
- break;
- }
- }
- break;
- case Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER:
- case Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT:
- nodeData["folder"] = true;
- break;
- case Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR:
- nodeData["separator"] = true;
- break;
- case Ci.nsINavHistoryResultNode.RESULT_TYPE_URI:
- nodeData["link"] = true;
- uri = NetUtil.newURI(node.uri);
- if (PlacesUtils.nodeIsBookmark(node)) {
- nodeData["bookmark"] = true;
- var parentNode = node.parent;
- if (parentNode) {
- if (PlacesUtils.nodeIsTagQuery(parentNode))
- nodeData["tagChild"] = true;
- }
- } else {
- var parentNode = node.parent;
- if (parentNode) {
- if (this.hasCachedLivemarkInfo(parentNode))
- nodeData["livemarkChild"] = true;
- }
- }
- break;
- }
-
- // annotations
- if (uri) {
- let names = PlacesUtils.annotations.getPageAnnotationNames(uri);
- for (let j = 0; j < names.length; ++j)
- nodeData[names[j]] = true;
- }
-
- // For items also include the item-specific annotations
- if (node.itemId != -1) {
- let names = PlacesUtils.annotations
- .getItemAnnotationNames(node.itemId);
- for (let j = 0; j < names.length; ++j)
- nodeData[names[j]] = true;
- }
- metadata.push(nodeData);
- }
-
- return metadata;
- },
-
- /**
- * Determines if a context-menu item should be shown
- * @param aMenuItem
- * the context menu item
- * @param aMetaData
- * meta data about the selection
- * @returns true if the conditions (see buildContextMenu) are satisfied
- * and the item can be displayed, false otherwise.
- */
- _shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) {
- var selectiontype = aMenuItem.getAttribute("selectiontype");
- if (!selectiontype) {
- selectiontype = "single|multiple";
- }
- var selectionTypes = selectiontype.split("|");
- if (selectionTypes.indexOf("any") != -1) {
- return true;
- }
- var count = aMetaData.length;
- if (count > 1 && selectionTypes.indexOf("multiple") == -1)
- return false;
- if (count == 1 && selectionTypes.indexOf("single") == -1)
- return false;
- // NB: if there is no selection, we show the item if (and only if)
- // the selectiontype includes 'none' - the metadata list will be
- // empty so none of the other criteria will apply anyway.
- if (count == 0)
- return selectionTypes.indexOf("none") != -1;
-
- var forceHideAttr = aMenuItem.getAttribute("forcehideselection");
- if (forceHideAttr) {
- var forceHideRules = forceHideAttr.split("|");
- for (let i = 0; i < aMetaData.length; ++i) {
- for (let j = 0; j < forceHideRules.length; ++j) {
- if (forceHideRules[j] in aMetaData[i])
- return false;
- }
- }
- }
-
- var selectionAttr = aMenuItem.getAttribute("selection");
- if (!selectionAttr) {
- return !aMenuItem.hidden;
- }
-
- if (selectionAttr == "any")
- return true;
-
- var showRules = selectionAttr.split("|");
- var anyMatched = false;
- function metaDataNodeMatches(metaDataNode, rules) {
- for (var i = 0; i < rules.length; i++) {
- if (rules[i] in metaDataNode)
- return true;
- }
- return false;
- }
-
- for (var i = 0; i < aMetaData.length; ++i) {
- if (metaDataNodeMatches(aMetaData[i], showRules))
- anyMatched = true;
- else
- return false;
- }
- return anyMatched;
- },
-
- /**
- * Detects information (meta-data rules) about the current selection in the
- * view (see _buildSelectionMetadata) and sets the visibility state for each
- * of the menu-items in the given popup with the following rules applied:
- * 1) The "selectiontype" attribute may be set on a menu-item to "single"
- * if the menu-item should be visible only if there is a single node
- * selected, or to "multiple" if the menu-item should be visible only if
- * multiple nodes are selected, or to "none" if the menuitems should be
- * visible for if there are no selected nodes, or to a |-separated
- * combination of these.
- * If the attribute is not set or set to an invalid value, the menu-item
- * may be visible irrespective of the selection.
- * 2) The "selection" attribute may be set on a menu-item to the various
- * meta-data rules for which it may be visible. The rules should be
- * separated with the | character.
- * 3) A menu-item may be visible only if at least one of the rules set in
- * its selection attribute apply to each of the selected nodes in the
- * view.
- * 4) The "forcehideselection" attribute may be set on a menu-item to rules
- * for which it should be hidden. This attribute takes priority over the
- * selection attribute. A menu-item would be hidden if at least one of the
- * given rules apply to one of the selected nodes. The rules should be
- * separated with the | character.
- * 5) The "hideifnoinsertionpoint" attribute may be set on a menu-item to
- * true if it should be hidden when there's no insertion point
- * 6) The visibility state of a menu-item is unchanged if none of these
- * attribute are set.
- * 7) These attributes should not be set on separators for which the
- * visibility state is "auto-detected."
- * 8) The "hideifprivatebrowsing" attribute may be set on a menu-item to
- * true if it should be hidden inside the private browsing mode
- * @param aPopup
- * The menupopup to build children into.
- * @return true if at least one item is visible, false otherwise.
- */
- buildContextMenu: function PC_buildContextMenu(aPopup) {
- var metadata = this._buildSelectionMetadata();
- var ip = this._view.insertionPoint;
- var noIp = !ip || ip.isTag;
-
- var separator = null;
- var visibleItemsBeforeSep = false;
- var usableItemCount = 0;
- for (var i = 0; i < aPopup.childNodes.length; ++i) {
- var item = aPopup.childNodes[i];
- if (item.localName != "menuseparator") {
- // We allow pasting into tag containers, so special case that.
- var hideIfNoIP = item.getAttribute("hideifnoinsertionpoint") == "true" &&
- noIp && !(ip && ip.isTag && item.id == "placesContext_paste");
- // Show the "Open Containing Folder" menu-item only when the context is
- // in the Library or in the Sidebar, and only when there's no insertion
- // point.
- var hideParentFolderItem = item.id == "placesContext_openParentFolder" &&
- (!/tree/i.test(this._view.localName) || ip);
- var hideIfPrivate = item.getAttribute("hideifprivatebrowsing") == "true" &&
- PrivateBrowsingUtils.isWindowPrivate(window);
- var shouldHideItem = hideIfNoIP || hideIfPrivate || hideParentFolderItem ||
- !this._shouldShowMenuItem(item, metadata);
- item.hidden = item.disabled = shouldHideItem;
-
- if (!item.hidden) {
- visibleItemsBeforeSep = true;
- usableItemCount++;
-
- // Show the separator above the menu-item if any
- if (separator) {
- separator.hidden = false;
- separator = null;
- }
- }
- }
- else { // menuseparator
- // Initially hide it. It will be unhidden if there will be at least one
- // visible menu-item above and below it.
- item.hidden = true;
-
- // We won't show the separator at all if no items are visible above it
- if (visibleItemsBeforeSep)
- separator = item;
-
- // New separator, count again:
- visibleItemsBeforeSep = false;
- }
- }
-
- // Set Open Folder/Links In Tabs items enabled state if they're visible
- if (usableItemCount > 0) {
- var openContainerInTabsItem = document.getElementById("placesContext_openContainer:tabs");
- if (!openContainerInTabsItem.hidden) {
- var containerToUse = this._view.selectedNode || this._view.result.root;
- if (PlacesUtils.nodeIsContainer(containerToUse)) {
- if (!PlacesUtils.hasChildURIs(containerToUse, true)) {
- openContainerInTabsItem.disabled = true;
- // Ensure that we don't display the menu if nothing is enabled:
- usableItemCount--;
- }
- }
- }
- }
-
- return usableItemCount > 0;
- },
-
- /**
- * Select all links in the current view.
- */
- selectAll: function PC_selectAll() {
- this._view.selectAll();
- },
-
- /**
- * Opens the bookmark properties for the selected URI Node.
- */
- showBookmarkPropertiesForSelection:
- function PC_showBookmarkPropertiesForSelection() {
- var node = this._view.selectedNode;
- if (!node)
- return;
-
- var itemType = PlacesUtils.nodeIsFolder(node) ||
- PlacesUtils.nodeIsTagQuery(node) ? "folder" : "bookmark";
- var concreteId = PlacesUtils.getConcreteItemId(node);
- var isRootItem = PlacesUtils.isRootItem(concreteId);
- var itemId = node.itemId;
- if (isRootItem || PlacesUtils.nodeIsTagQuery(node)) {
- // If this is a root or the Tags query we use the concrete itemId to catch
- // the correct title for the node.
- itemId = concreteId;
- }
-
- PlacesUIUtils.showBookmarkDialog({ action: "edit"
- , type: itemType
- , itemId: itemId
- , readOnly: isRootItem
- , hiddenRows: [ "folderPicker" ]
- }, window.top);
- },
-
- /**
- * This method can be run on a URI parameter to ensure that it didn't
- * receive a string instead of an nsIURI object.
- */
- _assertURINotString: function PC__assertURINotString(value) {
- NS_ASSERT((typeof(value) == "object") && !(value instanceof String),
- "This method should be passed a URI as a nsIURI object, not as a string.");
- },
-
- /**
- * Reloads the selected livemark if any.
- */
- reloadSelectedLivemark: function PC_reloadSelectedLivemark() {
- var selectedNode = this._view.selectedNode;
- if (selectedNode) {
- let itemId = selectedNode.itemId;
- PlacesUtils.livemarks.getLivemark({ id: itemId })
- .then(aLivemark => {
- aLivemark.reload(true);
- }, Components.utils.reportError);
- }
- },
-
- /**
- * Opens the links in the selected folder, or the selected links in new tabs.
- */
- openSelectionInTabs: function PC_openLinksInTabs(aEvent) {
- var node = this._view.selectedNode;
- var nodes = this._view.selectedNodes;
- // In the case of no selection, open the root node:
- if (!node && !nodes.length) {
- node = this._view.result.root;
- }
- if (node && PlacesUtils.nodeIsContainer(node))
- PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this._view);
- else
- PlacesUIUtils.openURINodesInTabs(nodes, aEvent, this._view);
- },
-
- /**
- * Shows the Add Bookmark UI for the current insertion point.
- *
- * @param aType
- * the type of the new item (bookmark/livemark/folder)
- */
- newItem: function PC_newItem(aType) {
- let ip = this._view.insertionPoint;
- if (!ip)
- throw Cr.NS_ERROR_NOT_AVAILABLE;
-
- let performed =
- PlacesUIUtils.showBookmarkDialog({ action: "add"
- , type: aType
- , defaultInsertionPoint: ip
- , hiddenRows: [ "folderPicker" ]
- }, window.top);
- if (performed) {
- // Select the new item.
- let insertedNodeId = PlacesUtils.bookmarks
- .getIdForItemAt(ip.itemId, ip.index);
- this._view.selectItems([insertedNodeId], false);
- }
- },
-
- /**
- * Create a new Bookmark separator somewhere.
- */
- newSeparator: function PC_newSeparator() {
- var ip = this._view.insertionPoint;
- if (!ip)
- throw Cr.NS_ERROR_NOT_AVAILABLE;
- var txn = new PlacesCreateSeparatorTransaction(ip.itemId, ip.index);
- PlacesUtils.transactionManager.doTransaction(txn);
- // select the new item
- var insertedNodeId = PlacesUtils.bookmarks
- .getIdForItemAt(ip.itemId, ip.index);
- this._view.selectItems([insertedNodeId], false);
- },
-
- /**
- * Opens a dialog for moving the selected nodes.
- */
- moveSelectedBookmarks: function PC_moveBookmarks() {
- window.openDialog("chrome://browser/content/places/moveBookmarks.xul",
- "", "chrome, modal",
- this._view.selectedNodes);
- },
-
- /**
- * Sort the selected folder by name.
- */
- sortFolderByName: function PC_sortFolderByName() {
- var itemId = PlacesUtils.getConcreteItemId(this._view.selectedNode);
- var txn = new PlacesSortFolderByNameTransaction(itemId);
- PlacesUtils.transactionManager.doTransaction(txn);
- },
-
- /**
- * Open the parent folder for the selected bookmarks search result.
- */
- openParentFolder: function PC_openParentFolder() {
- var view;
- if (!document.popupNode) {
- view = document.commandDispatcher.focusedElement;
- } else {
- view = PlacesUIUtils.getViewForNode(document.popupNode); // XULElement
- }
- if (!view || view.getAttribute("type") != "places")
- return;
- var node = view.selectedNode; // nsINavHistoryResultNode
- var aItemId = node.itemId;
- var aFolderItemId = this.getParentFolderByItemId(aItemId);
- if (aFolderItemId)
- this.selectFolderByItemId(view, aFolderItemId, aItemId);
- },
-
- getParentFolderByItemId: function PC_getParentFolderByItemId(aItemId) {
- var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].
- getService(Components.interfaces.nsINavBookmarksService);
- var parentFolderId = bmsvc.getFolderIdForItem(aItemId);
-
- return parentFolderId;
- },
-
- selectItems2: function PC_selectItems2(view, aIDs) {
- var ids = aIDs; // Don't manipulate the caller's array.
-
- // Array of nodes found by findNodes which are to be selected
- var nodes = [];
-
- // Array of nodes found by findNodes which should be opened
- var nodesToOpen = [];
-
- // A set of URIs of container-nodes that were previously searched,
- // and thus shouldn't be searched again. This is empty at the initial
- // start of the recursion and gets filled in as the recursion
- // progresses.
- var nodesURIChecked = [];
-
- /**
- * Recursively search through a node's children for items
- * with the given IDs. When a matching item is found, remove its ID
- * from the IDs array, and add the found node to the nodes dictionary.
- *
- * NOTE: This method will leave open any node that had matching items
- * in its subtree.
- */
- function findNodes(node) {
- var foundOne = false;
- // See if node matches an ID we wanted; add to results.
- // For simple folder queries, check both itemId and the concrete
- // item id.
- var index = ids.indexOf(node.itemId);
- if (index == -1 &&
- node.type == Components.interfaces.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) {
- index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId); //xxx Bug 556739 3.7a5pre
- }
-
- if (index != -1) {
- nodes.push(node);
- foundOne = true;
- ids.splice(index, 1);
- }
-
- if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) ||
- nodesURIChecked.indexOf(node.uri) != -1)
- return foundOne;
-
- nodesURIChecked.push(node.uri);
- PlacesUtils.asContainer(node); // xxx Bug 556739 3.7a6pre
-
- // Remember the beginning state so that we can re-close
- // this node if we don't find any additional results here.
- var previousOpenness = node.containerOpen;
- node.containerOpen = true;
- for (var child = 0; child < node.childCount && ids.length > 0;
- child++) {
- var childNode = node.getChild(child);
- var found = findNodes(childNode);
- if (!foundOne)
- foundOne = found;
- }
-
- // If we didn't find any additional matches in this node's
- // subtree, revert the node to its previous openness.
- if (foundOne)
- nodesToOpen.unshift(node);
- node.containerOpen = previousOpenness;
- return foundOne;
- } // findNodes
-
- // Disable notifications while looking for nodes.
- let result = view.result;
- let didSuppressNotifications = result.suppressNotifications;
- if (!didSuppressNotifications)
- result.suppressNotifications = true
- try {
- findNodes(view.result.root);
- }
- finally {
- if (!didSuppressNotifications)
- result.suppressNotifications = false;
- }
-
- // For all the nodes we've found, highlight the corresponding
- // index in the tree.
- var resultview = view.view;
- var selection = resultview.selection;
- selection.selectEventsSuppressed = true;
- selection.clearSelection();
- // Open nodes containing found items.
- for (var i = 0; i < nodesToOpen.length; i++) {
- nodesToOpen[i].containerOpen = true;
- }
- for (var i = 0; i < nodes.length; i++) {
- if (PlacesUtils.nodeIsContainer(nodes[i]))
- continue;
-
- var index = resultview.treeIndexForNode(nodes[i]);
- selection.rangedSelect(index, index, true);
- }
- selection.selectEventsSuppressed = false;
- },
-
- selectFolderByItemId: function PC_selectFolderByItemId(view, aFolderItemId, aItemId) {
- // Library
- if (view.getAttribute("id") == "placeContent") {
- view = document.getElementById("placesList");
- // Select a folder node in folder pane.
- this.selectItems2(view, [aFolderItemId]);
- view.selectItems([aFolderItemId]);
- if (view.currentIndex)
- view.treeBoxObject.ensureRowIsVisible(view.currentIndex);
- // Reselect child node.
- setTimeout(function(aItemId, view) {
- var aView = view.ownerDocument.getElementById("placeContent");
- aView.selectItems([aItemId]);
- if (aView.currentIndex)
- aView.treeBoxObject.ensureRowIsVisible(aView.currentIndex);
- }, 0, aItemId, view);
- return;
- }
-
- // Bookmarks Sidebar
- if (!view)
- return;
- view.place = view.place;
-
- if ('FlatBookmarksOverlay' in window) {
- var sidebarwin = view.ownerDocument.defaultView;
- var searchBox = sidebarwin.document.getElementById("search-box");
- searchBox.value = "";
- searchBox.doCommand();
- sidebarwin.FlatBookmarks._setTreePlace(sidebarwin.FlatBookmarks._makePlaceForFolder(aFolderItemId));
- view.selectItems([aItemId]);
- var tbo = view.treeBoxObject;
- tbo.ensureRowIsVisible(view.currentIndex);
- view.focus();
- return;
- }
-
- view.findNode = function flatChildNodes(node, aIDs) {
- var ids = aIDs; // Don't manipulate the caller's array.
-
- // Array of nodes found by findNodes which are to be selected
- var nodes = [];
-
- // Array of nodes found by findNodes which should be opened
- var nodesToOpen = [];
-
- // A set of URIs of container-nodes that were previously searched,
- // and thus shouldn't be searched again. This is empty at the initial
- // start of the recursion and gets filled in as the recursion
- // progresses.
- var nodesURIChecked = [];
-
- /**
- * Recursively search through a node's children for items
- * with the given IDs. When a matching item is found, remove its ID
- * from the IDs array, and add the found node to the nodes dictionary.
- *
- * NOTE: This method will leave open any node that had matching items
- * in its subtree.
- */
- function findNodes(node) {
- var foundOne = false;
- // See if node matches an ID we wanted; add to results.
- // For simple folder queries, check both itemId and the concrete
- // item id.
- var index = ids.indexOf(node.itemId);
- if (index == -1 &&
- node.type == Components.interfaces.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) {
- index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId); // xxx Bug 556739 3.7a5pre
- }
-
- if (index != -1) {
- nodes.push(node);
- foundOne = true;
- ids.splice(index, 1);
- }
-
- if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) ||
- nodesURIChecked.indexOf(node.uri) != -1)
- return foundOne;
-
- nodesURIChecked.push(node.uri);
- PlacesUtils.asContainer(node); // xxx Bug 556739 3.7a6pre
- // Remember the beginning state so that we can re-close
- // this node if we don't find any additional results here.
- var previousOpenness = node.containerOpen;
- node.containerOpen = true;
- for (var child = 0; child < node.childCount && ids.length > 0;
- child++) {
- var childNode = node.getChild(child);
- if (PlacesUtils.nodeIsQuery(childNode))
- continue;
- var found = findNodes(childNode);
- if (!foundOne)
- foundOne = found;
- }
-
- // If we didn't find any additional matches in this node's
- // subtree, revert the node to its previous openness.
- if (foundOne)
- nodesToOpen.unshift(node);
- node.containerOpen = previousOpenness;
- return foundOne;
- } // findNodes
-
- // Disable notifications while looking for nodes.
- let result = this.result;
- let didSuppressNotifications = result.suppressNotifications;
- if (!didSuppressNotifications)
- result.suppressNotifications = true
- try {
- findNodes(this.result.root);
- }
- finally {
- if (!didSuppressNotifications)
- result.suppressNotifications = false;
- }
-
- // Open nodes containing found items.
- for (var i = 0; i < nodesToOpen.length; i++) {
- nodesToOpen[i].containerOpen = true;
- }
- return nodes;
- }; // findNode
-
- // For all the nodes we've found, highlight the corresponding
- // index in the tree.
- var resultview = view.view;
- var selection = view.view.selection;
- selection.selectEventsSuppressed = true;
- selection.clearSelection();
- var nodes = view.findNode(view.result.root, [aFolderItemId]);
- if (nodes.length > 0) {
- var index = resultview.treeIndexForNode(nodes[0]);
- nodes = view.findNode(nodes[0], [aItemId]);
- if (nodes.length > 0) {
- index = resultview.treeIndexForNode(nodes[0]);
- selection.rangedSelect(index, index, true);
- }
- }
- selection.selectEventsSuppressed = false;
-
- var tbo = view.treeBoxObject;
- tbo.ensureRowIsVisible(view.currentIndex);
- view.focus();
- return;
- },
-
- /**
- * Walk the list of folders we're removing in this delete operation, and
- * see if the selected node specified is already implicitly being removed
- * because it is a child of that folder.
- * @param node
- * Node to check for containment.
- * @param pastFolders
- * List of folders the calling function has already traversed
- * @returns true if the node should be skipped, false otherwise.
- */
- _shouldSkipNode: function PC_shouldSkipNode(node, pastFolders) {
- /**
- * Determines if a node is contained by another node within a resultset.
- * @param node
- * The node to check for containment for
- * @param parent
- * The parent container to check for containment in
- * @returns true if node is a member of parent's children, false otherwise.
- */
- function isContainedBy(node, parent) {
- var cursor = node.parent;
- while (cursor) {
- if (cursor == parent)
- return true;
- cursor = cursor.parent;
- }
- return false;
- }
-
- for (var j = 0; j < pastFolders.length; ++j) {
- if (isContainedBy(node, pastFolders[j]))
- return true;
- }
- return false;
- },
-
- /**
- * Creates a set of transactions for the removal of a range of items.
- * A range is an array of adjacent nodes in a view.
- * @param [in] range
- * An array of nodes to remove. Should all be adjacent.
- * @param [out] transactions
- * An array of transactions.
- * @param [optional] removedFolders
- * An array of folder nodes that have already been removed.
- */
- _removeRange: function PC__removeRange(range, transactions, removedFolders) {
- NS_ASSERT(transactions instanceof Array, "Must pass a transactions array");
- if (!removedFolders)
- removedFolders = [];
-
- for (var i = 0; i < range.length; ++i) {
- var node = range[i];
- if (this._shouldSkipNode(node, removedFolders))
- continue;
-
- if (PlacesUtils.nodeIsTagQuery(node.parent)) {
- // This is a uri node inside a tag container. It needs a special
- // untag transaction.
- var tagItemId = PlacesUtils.getConcreteItemId(node.parent);
- var uri = NetUtil.newURI(node.uri);
- let txn = new PlacesUntagURITransaction(uri, [tagItemId]);
- transactions.push(txn);
- }
- else if (PlacesUtils.nodeIsTagQuery(node) && node.parent &&
- PlacesUtils.nodeIsQuery(node.parent) &&
- PlacesUtils.asQuery(node.parent).queryOptions.resultType ==
- Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY) {
- // This is a tag container.
- // Untag all URIs tagged with this tag only if the tag container is
- // child of the "Tags" query in the library, in all other places we
- // must only remove the query node.
- var tag = node.title;
- var URIs = PlacesUtils.tagging.getURIsForTag(tag);
- for (var j = 0; j < URIs.length; j++) {
- let txn = new PlacesUntagURITransaction(URIs[j], [tag]);
- transactions.push(txn);
- }
- }
- else if (PlacesUtils.nodeIsURI(node) &&
- PlacesUtils.nodeIsQuery(node.parent) &&
- PlacesUtils.asQuery(node.parent).queryOptions.queryType ==
- Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
- // This is a uri node inside an history query.
- PlacesUtils.bhistory.removePage(NetUtil.newURI(node.uri));
- // History deletes are not undoable, so we don't have a transaction.
- }
- else if (node.itemId == -1 &&
- PlacesUtils.nodeIsQuery(node) &&
- PlacesUtils.asQuery(node).queryOptions.queryType ==
- Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
- // This is a dynamically generated history query, like queries
- // grouped by site, time or both. Dynamically generated queries don't
- // have an itemId even if they are descendants of a bookmark.
- this._removeHistoryContainer(node);
- // History deletes are not undoable, so we don't have a transaction.
- }
- else {
- // This is a common bookmark item.
- if (PlacesUtils.nodeIsFolder(node)) {
- // If this is a folder we add it to our array of folders, used
- // to skip nodes that are children of an already removed folder.
- removedFolders.push(node);
- }
- let txn = new PlacesRemoveItemTransaction(node.itemId);
- transactions.push(txn);
- }
- }
- },
-
- /**
- * Removes the set of selected ranges from bookmarks.
- * @param txnName
- * See |remove|.
- */
- _removeRowsFromBookmarks: function PC__removeRowsFromBookmarks(txnName) {
- var ranges = this._view.removableSelectionRanges;
- var transactions = [];
- var removedFolders = [];
-
- for (var i = 0; i < ranges.length; i++)
- this._removeRange(ranges[i], transactions, removedFolders);
-
- if (transactions.length > 0) {
- var txn = new PlacesAggregatedTransaction(txnName, transactions);
- PlacesUtils.transactionManager.doTransaction(txn);
- }
- },
-
- /**
- * Removes the set of selected ranges from history.
- *
- * @note history deletes are not undoable.
- */
- _removeRowsFromHistory: function PC__removeRowsFromHistory() {
- let nodes = this._view.selectedNodes;
- let URIs = [];
- for (let i = 0; i < nodes.length; ++i) {
- let node = nodes[i];
- if (PlacesUtils.nodeIsURI(node)) {
- let uri = NetUtil.newURI(node.uri);
- // Avoid duplicates.
- if (URIs.indexOf(uri) < 0) {
- URIs.push(uri);
- }
- }
- else if (PlacesUtils.nodeIsQuery(node) &&
- PlacesUtils.asQuery(node).queryOptions.queryType ==
- Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
- this._removeHistoryContainer(node);
- }
- }
-
- // Do removal in chunks to give some breath to main-thread.
- function pagesChunkGenerator(aURIs) {
- while (aURIs.length) {
- let URIslice = aURIs.splice(0, REMOVE_PAGES_CHUNKLEN);
- PlacesUtils.bhistory.removePages(URIslice, URIslice.length);
- Services.tm.mainThread.dispatch(function() {
- try {
- gen.next();
- } catch (ex if ex instanceof StopIteration) {}
- }, Ci.nsIThread.DISPATCH_NORMAL);
- yield;
- }
- }
- let gen = pagesChunkGenerator(URIs);
- gen.next();
- },
-
- /**
- * Removes history visits for an history container node.
- * @param [in] aContainerNode
- * The container node to remove.
- *
- * @note history deletes are not undoable.
- */
- _removeHistoryContainer: function PC__removeHistoryContainer(aContainerNode) {
- if (PlacesUtils.nodeIsHost(aContainerNode)) {
- // Site container.
- PlacesUtils.bhistory.removePagesFromHost(aContainerNode.title, true);
- }
- else if (PlacesUtils.nodeIsDay(aContainerNode)) {
- // Day container.
- let query = aContainerNode.getQueries()[0];
- let beginTime = query.beginTime;
- let endTime = query.endTime;
- NS_ASSERT(query && beginTime && endTime,
- "A valid date container query should exist!");
- // We want to exclude beginTime from the removal because
- // removePagesByTimeframe includes both extremes, while date containers
- // exclude the lower extreme. So, if we would not exclude it, we would
- // end up removing more history than requested.
- PlacesUtils.bhistory.removePagesByTimeframe(beginTime + 1, endTime);
- }
- },
-
- /**
- * Removes the selection
- * @param aTxnName
- * A name for the transaction if this is being performed
- * as part of another operation.
- */
- remove: function PC_remove(aTxnName) {
- if (!this._hasRemovableSelection())
- return;
-
- NS_ASSERT(aTxnName !== undefined, "Must supply Transaction Name");
-
- var root = this._view.result.root;
-
- if (PlacesUtils.nodeIsFolder(root))
- this._removeRowsFromBookmarks(aTxnName);
- else if (PlacesUtils.nodeIsQuery(root)) {
- var queryType = PlacesUtils.asQuery(root).queryOptions.queryType;
- if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS)
- this._removeRowsFromBookmarks(aTxnName);
- else if (queryType == Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
- this._removeRowsFromHistory();
- else
- NS_ASSERT(false, "implement support for QUERY_TYPE_UNIFIED");
- }
- else
- NS_ASSERT(false, "unexpected root");
- },
-
- /**
- * Fills a DataTransfer object with the content of the selection that can be
- * dropped elsewhere.
- * @param aEvent
- * The dragstart event.
- */
- setDataTransfer: function PC_setDataTransfer(aEvent) {
- let dt = aEvent.dataTransfer;
- let doCopy = ["copyLink", "copy", "link"].indexOf(dt.effectAllowed) != -1;
-
- let result = this._view.result;
- let didSuppressNotifications = result.suppressNotifications;
- if (!didSuppressNotifications)
- result.suppressNotifications = true;
-
- function addData(type, index, feedURI) {
- let wrapNode = PlacesUtils.wrapNode(node, type, feedURI);
- dt.mozSetDataAt(type, wrapNode, index);
- }
-
- function addURIData(index, feedURI) {
- addData(PlacesUtils.TYPE_X_MOZ_URL, index, feedURI);
- addData(PlacesUtils.TYPE_UNICODE, index, feedURI);
- addData(PlacesUtils.TYPE_HTML, index, feedURI);
- }
-
- try {
- let nodes = this._view.draggableSelection;
- for (let i = 0; i < nodes.length; ++i) {
- var node = nodes[i];
-
- // This order is _important_! It controls how this and other
- // applications select data to be inserted based on type.
- addData(PlacesUtils.TYPE_X_MOZ_PLACE, i);
-
- // Drop the feed uri for livemark containers
- let livemarkInfo = this.getCachedLivemarkInfo(node);
- if (livemarkInfo) {
- addURIData(i, livemarkInfo.feedURI.spec);
- }
- else if (node.uri) {
- addURIData(i);
- }
- }
- }
- finally {
- if (!didSuppressNotifications)
- result.suppressNotifications = false;
- }
- },
-
- get clipboardAction () {
- let action = {};
- let actionOwner;
- try {
- let xferable = Cc["@mozilla.org/widget/transferable;1"].
- createInstance(Ci.nsITransferable);
- xferable.init(null);
- xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION)
- this.clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
- xferable.getTransferData(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION, action, {});
- [action, actionOwner] =
- action.value.QueryInterface(Ci.nsISupportsString).data.split(",");
- } catch(ex) {
- // Paste from external sources don't have any associated action, just
- // fallback to a copy action.
- return "copy";
- }
- // For cuts also check who inited the action, since cuts across different
- // instances should instead be handled as copies (The sources are not
- // available for this instance).
- if (action == "cut" && actionOwner != this.profileName)
- action = "copy";
-
- return action;
- },
-
- _releaseClipboardOwnership: function PC__releaseClipboardOwnership() {
- if (this.cutNodes.length > 0) {
- // This clears the logical clipboard, doesn't remove data.
- this.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
- }
- },
-
- _clearClipboard: function PC__clearClipboard() {
- let xferable = Cc["@mozilla.org/widget/transferable;1"].
- createInstance(Ci.nsITransferable);
- xferable.init(null);
- // Empty transferables may cause crashes, so just add an unknown type.
- const TYPE = "text/x-moz-place-empty";
- xferable.addDataFlavor(TYPE);
- xferable.setTransferData(TYPE, PlacesUtils.toISupportsString(""), 0);
- this.clipboard.setData(xferable, null, Ci.nsIClipboard.kGlobalClipboard);
- },
-
- _populateClipboard: function PC__populateClipboard(aNodes, aAction) {
- // This order is _important_! It controls how this and other applications
- // select data to be inserted based on type.
- let contents = [
- { type: PlacesUtils.TYPE_X_MOZ_PLACE, entries: [] },
- { type: PlacesUtils.TYPE_X_MOZ_URL, entries: [] },
- { type: PlacesUtils.TYPE_HTML, entries: [] },
- { type: PlacesUtils.TYPE_UNICODE, entries: [] },
- ];
-
- // Avoid handling descendants of a copied node, the transactions take care
- // of them automatically.
- let copiedFolders = [];
- aNodes.forEach(function (node) {
- if (this._shouldSkipNode(node, copiedFolders))
- return;
- if (PlacesUtils.nodeIsFolder(node))
- copiedFolders.push(node);
-
- let livemarkInfo = this.getCachedLivemarkInfo(node);
- let feedURI = livemarkInfo && livemarkInfo.feedURI.spec;
-
- contents.forEach(function (content) {
- content.entries.push(
- PlacesUtils.wrapNode(node, content.type, feedURI)
- );
- });
- }, this);
-
- function addData(type, data) {
- xferable.addDataFlavor(type);
- xferable.setTransferData(type, PlacesUtils.toISupportsString(data),
- data.length * 2);
- }
-
- let xferable = Cc["@mozilla.org/widget/transferable;1"].
- createInstance(Ci.nsITransferable);
- xferable.init(null);
- let hasData = false;
- // This order matters here! It controls how this and other applications
- // select data to be inserted based on type.
- contents.forEach(function (content) {
- if (content.entries.length > 0) {
- hasData = true;
- let glue =
- content.type == PlacesUtils.TYPE_X_MOZ_PLACE ? "," : PlacesUtils.endl;
- addData(content.type, content.entries.join(glue));
- }
- });
-
- // Track the exected action in the xferable. This must be the last flavor
- // since it's the least preferred one.
- // Enqueue a unique instance identifier to distinguish operations across
- // concurrent instances of the application.
- addData(PlacesUtils.TYPE_X_MOZ_PLACE_ACTION, aAction + "," + this.profileName);
-
- if (hasData) {
- this.clipboard.setData(xferable,
- this.cutNodes.length > 0 ? this : null,
- Ci.nsIClipboard.kGlobalClipboard);
- }
- },
-
- _cutNodes: [],
- get cutNodes() this._cutNodes,
- set cutNodes(aNodes) {
- let self = this;
- function updateCutNodes(aValue) {
- self._cutNodes.forEach(function (aNode) {
- self._view.toggleCutNode(aNode, aValue);
- });
- }
-
- updateCutNodes(false);
- this._cutNodes = aNodes;
- updateCutNodes(true);
- return aNodes;
- },
-
- /**
- * Copy Bookmarks and Folders to the clipboard
- */
- copy: function PC_copy() {
- let result = this._view.result;
- let didSuppressNotifications = result.suppressNotifications;
- if (!didSuppressNotifications)
- result.suppressNotifications = true;
- try {
- this._populateClipboard(this._view.selectedNodes, "copy");
- }
- finally {
- if (!didSuppressNotifications)
- result.suppressNotifications = false;
- }
- },
-
- /**
- * Cut Bookmarks and Folders to the clipboard
- */
- cut: function PC_cut() {
- let result = this._view.result;
- let didSuppressNotifications = result.suppressNotifications;
- if (!didSuppressNotifications)
- result.suppressNotifications = true;
- try {
- this._populateClipboard(this._view.selectedNodes, "cut");
- this.cutNodes = this._view.selectedNodes;
- }
- finally {
- if (!didSuppressNotifications)
- result.suppressNotifications = false;
- }
- },
-
- /**
- * Paste Bookmarks and Folders from the clipboard
- */
- paste: function PC_paste() {
- // No reason to proceed if there isn't a valid insertion point.
- let ip = this._view.insertionPoint;
- if (!ip)
- throw Cr.NS_ERROR_NOT_AVAILABLE;
-
- let action = this.clipboardAction;
-
- let xferable = Cc["@mozilla.org/widget/transferable;1"].
- createInstance(Ci.nsITransferable);
- xferable.init(null);
- // This order matters here! It controls the preferred flavors for this
- // paste operation.
- [ PlacesUtils.TYPE_X_MOZ_PLACE,
- PlacesUtils.TYPE_X_MOZ_URL,
- PlacesUtils.TYPE_UNICODE,
- ].forEach(function (type) xferable.addDataFlavor(type));
-
- this.clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
-
- // Now get the clipboard contents, in the best available flavor.
- let data = {}, type = {}, items = [];
- try {
- xferable.getAnyTransferData(type, data, {});
- data = data.value.QueryInterface(Ci.nsISupportsString).data;
- type = type.value;
- items = PlacesUtils.unwrapNodes(data, type);
- } catch(ex) {
- // No supported data exists or nodes unwrap failed, just bail out.
- return;
- }
-
- let transactions = [];
- let insertionIndex = ip.index;
- for (let i = 0; i < items.length; ++i) {
- if (ip.isTag) {
- // Pasting into a tag container means tagging the item, regardless of
- // the requested action.
- let tagTxn = new PlacesTagURITransaction(NetUtil.newURI(items[i].uri),
- [ip.itemId]);
- transactions.push(tagTxn);
- continue;
- }
-
- // Adjust index to make sure items are pasted in the correct position.
- // If index is DEFAULT_INDEX, items are just appended.
- if (ip.index != PlacesUtils.bookmarks.DEFAULT_INDEX)
- insertionIndex = ip.index + i;
-
- transactions.push(
- PlacesUIUtils.makeTransaction(items[i], type, ip.itemId,
- insertionIndex, action == "copy")
- );
- }
-
- let aggregatedTxn = new PlacesAggregatedTransaction("Paste", transactions);
- PlacesUtils.transactionManager.doTransaction(aggregatedTxn);
-
- // Cut/past operations are not repeatable, so clear the clipboard.
- if (action == "cut") {
- this._clearClipboard();
- }
-
- // Select the pasted items, they should be consecutive.
- let insertedNodeIds = [];
- for (let i = 0; i < transactions.length; ++i) {
- insertedNodeIds.push(
- PlacesUtils.bookmarks.getIdForItemAt(ip.itemId, ip.index + i)
- );
- }
- if (insertedNodeIds.length > 0)
- this._view.selectItems(insertedNodeIds, false);
- },
-
- /**
- * Cache the livemark info for a node. This allows the controller and the
- * views to treat the given node as a livemark.
- * @param aNode
- * a places result node.
- * @param aLivemarkInfo
- * a mozILivemarkInfo object.
- */
- cacheLivemarkInfo: function PC_cacheLivemarkInfo(aNode, aLivemarkInfo) {
- this._cachedLivemarkInfoObjects.set(aNode, aLivemarkInfo);
- },
-
- /**
- * Returns whether or not there's cached mozILivemarkInfo object for a node.
- * @param aNode
- * a places result node.
- * @return true if there's a cached mozILivemarkInfo object for
- * aNode, false otherwise.
- */
- hasCachedLivemarkInfo: function PC_hasCachedLivemarkInfo(aNode)
- this._cachedLivemarkInfoObjects.has(aNode),
-
- /**
- * Returns the cached livemark info for a node, if set by cacheLivemarkInfo,
- * null otherwise.
- * @param aNode
- * a places result node.
- * @return the mozILivemarkInfo object for aNode, if set, null otherwise.
- */
- getCachedLivemarkInfo: function PC_getCachedLivemarkInfo(aNode)
- this._cachedLivemarkInfoObjects.get(aNode, null)
-};
-
-/**
- * Handles drag and drop operations for views. Note that this is view agnostic!
- * You should not use PlacesController._view within these methods, since
- * the view that the item(s) have been dropped on was not necessarily active.
- * Drop functions are passed the view that is being dropped on.
- */
-var PlacesControllerDragHelper = {
- /**
- * DOM Element currently being dragged over
- */
- currentDropTarget: null,
-
- /**
- * Determines if the mouse is currently being dragged over a child node of
- * this menu. This is necessary so that the menu doesn't close while the
- * mouse is dragging over one of its submenus
- * @param node
- * The container node
- * @returns true if the user is dragging over a node within the hierarchy of
- * the container, false otherwise.
- */
- draggingOverChildNode: function PCDH_draggingOverChildNode(node) {
- let currentNode = this.currentDropTarget;
- while (currentNode) {
- if (currentNode == node)
- return true;
- currentNode = currentNode.parentNode;
- }
- return false;
- },
-
- /**
- * @returns The current active drag session. Returns null if there is none.
- */
- getSession: function PCDH__getSession() {
- return this.dragService.getCurrentSession();
- },
-
- /**
- * Extract the first accepted flavor from a list of flavors.
- * @param aFlavors
- * The flavors list of type nsIDOMDOMStringList.
- */
- getFirstValidFlavor: function PCDH_getFirstValidFlavor(aFlavors) {
- for (let i = 0; i < aFlavors.length; i++) {
- if (this.GENERIC_VIEW_DROP_TYPES.indexOf(aFlavors[i]) != -1)
- return aFlavors[i];
- }
-
- // If no supported flavor is found, check if data includes text/plain
- // contents. If so, request them as text/unicode, a conversion will happen
- // automatically.
- if (aFlavors.contains("text/plain")) {
- return PlacesUtils.TYPE_UNICODE;
- }
-
- return null;
- },
-
- /**
- * Determines whether or not the data currently being dragged can be dropped
- * on a places view.
- * @param ip
- * The insertion point where the items should be dropped.
- */
- canDrop: function PCDH_canDrop(ip, dt) {
- let dropCount = dt.mozItemCount;
-
- // Check every dragged item.
- for (let i = 0; i < dropCount; i++) {
- let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
- if (!flavor)
- return false;
-
- // Urls can be dropped on any insertionpoint.
- // XXXmano: remember that this method is called for each dragover event!
- // Thus we shouldn't use unwrapNodes here at all if possible.
- // I think it would be OK to accept bogus data here (e.g. text which was
- // somehow wrapped as TAB_DROP_TYPE, this is not in our control, and
- // will just case the actual drop to be a no-op), and only rule out valid
- // expected cases, which are either unsupported flavors, or items which
- // cannot be dropped in the current insertionpoint. The last case will
- // likely force us to use unwrapNodes for the private data types of
- // places.
- if (flavor == TAB_DROP_TYPE)
- continue;
-
- let data = dt.mozGetDataAt(flavor, i);
- let dragged;
- try {
- dragged = PlacesUtils.unwrapNodes(data, flavor)[0];
- }
- catch (e) {
- return false;
- }
-
- // Only bookmarks and urls can be dropped into tag containers.
- if (ip.isTag && ip.orientation == Ci.nsITreeView.DROP_ON &&
- dragged.type != PlacesUtils.TYPE_X_MOZ_URL &&
- (dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE ||
- (dragged.uri && dragged.uri.startsWith("place:")) ))
- return false;
-
- // The following loop disallows the dropping of a folder on itself or
- // on any of its descendants.
- if (dragged.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER ||
- (dragged.uri && dragged.uri.startsWith("place:")) ) {
- let parentId = ip.itemId;
- while (parentId != PlacesUtils.placesRootId) {
- if (dragged.concreteId == parentId || dragged.id == parentId)
- return false;
- parentId = PlacesUtils.bookmarks.getFolderIdForItem(parentId);
- }
- }
- }
- return true;
- },
-
-
- /**
- * Determines if a node can be moved.
- *
- * @param aNode
- * A nsINavHistoryResultNode node.
- * @returns True if the node can be moved, false otherwise.
- */
- canMoveNode:
- function PCDH_canMoveNode(aNode) {
- // Only bookmark items are movable.
- if (aNode.itemId == -1)
- return false;
-
- // Once tags and bookmarked are divorced, the tag-query check should be
- // removed.
- let parentNode = aNode.parent;
- return parentNode != null &&
- !(PlacesUtils.nodeIsFolder(parentNode) &&
- PlacesUIUtils.isContentsReadOnly(parentNode)) &&
- !PlacesUtils.nodeIsTagQuery(parentNode);
- },
-
- /**
- * Handles the drop of one or more items onto a view.
- * @param insertionPoint
- * The insertion point where the items should be dropped
- */
- onDrop: function PCDH_onDrop(insertionPoint, dt) {
- let doCopy = ["copy", "link"].indexOf(dt.dropEffect) != -1;
-
- let transactions = [];
- let dropCount = dt.mozItemCount;
- let movedCount = 0;
- for (let i = 0; i < dropCount; ++i) {
- let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
- if (!flavor)
- return;
-
- let data = dt.mozGetDataAt(flavor, i);
- let unwrapped;
- if (flavor != TAB_DROP_TYPE) {
- // There's only ever one in the D&D case.
- unwrapped = PlacesUtils.unwrapNodes(data, flavor)[0];
- }
- else if (data instanceof XULElement && data.localName == "tab" &&
- data.ownerDocument.defaultView instanceof ChromeWindow) {
- let uri = data.linkedBrowser.currentURI;
- let spec = uri ? uri.spec : "about:blank";
- let title = data.label;
- unwrapped = { uri: spec,
- title: data.label,
- type: PlacesUtils.TYPE_X_MOZ_URL};
- }
- else
- throw("bogus data was passed as a tab");
-
- let index = insertionPoint.index;
-
- // Adjust insertion index to prevent reversal of dragged items. When you
- // drag multiple elts upward: need to increment index or each successive
- // elt will be inserted at the same index, each above the previous.
- let dragginUp = insertionPoint.itemId == unwrapped.parent &&
- index < PlacesUtils.bookmarks.getItemIndex(unwrapped.id);
- if (index != -1 && dragginUp)
- index += movedCount++;
-
- // If dragging over a tag container we should tag the item.
- if (insertionPoint.isTag &&
- insertionPoint.orientation == Ci.nsITreeView.DROP_ON) {
- let uri = NetUtil.newURI(unwrapped.uri);
- let tagItemId = insertionPoint.itemId;
- let tagTxn = new PlacesTagURITransaction(uri, [tagItemId]);
- transactions.push(tagTxn);
- }
- else {
- transactions.push(PlacesUIUtils.makeTransaction(unwrapped,
- flavor, insertionPoint.itemId,
- index, doCopy));
- }
- }
-
- let txn = new PlacesAggregatedTransaction("DropItems", transactions);
- PlacesUtils.transactionManager.doTransaction(txn);
- },
-
- /**
- * Checks if we can insert into a container.
- * @param aContainer
- * The container were we are want to drop
- */
- disallowInsertion: function(aContainer) {
- NS_ASSERT(aContainer, "empty container");
- // Allow dropping into Tag containers and editable folders.
- return !PlacesUtils.nodeIsTagQuery(aContainer) &&
- (!PlacesUtils.nodeIsFolder(aContainer) ||
- PlacesUIUtils.isContentsReadOnly(aContainer));
- },
-
- placesFlavors: [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
- PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
- PlacesUtils.TYPE_X_MOZ_PLACE],
-
- // The order matters.
- GENERIC_VIEW_DROP_TYPES: [PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER,
- PlacesUtils.TYPE_X_MOZ_PLACE_SEPARATOR,
- PlacesUtils.TYPE_X_MOZ_PLACE,
- PlacesUtils.TYPE_X_MOZ_URL,
- TAB_DROP_TYPE,
- PlacesUtils.TYPE_UNICODE],
-};
-
-
-XPCOMUtils.defineLazyServiceGetter(PlacesControllerDragHelper, "dragService",
- "@mozilla.org/widget/dragservice;1",
- "nsIDragService");
-
-function goUpdatePlacesCommands() {
- // Get the controller for one of the places commands.
- var placesController = doGetPlacesControllerForCommand("placesCmd_open");
- function updatePlacesCommand(aCommand) {
- goSetCommandEnabled(aCommand, placesController &&
- placesController.isCommandEnabled(aCommand));
- }
-
- updatePlacesCommand("placesCmd_open");
- updatePlacesCommand("placesCmd_open:window");
- updatePlacesCommand("placesCmd_open:privatewindow");
- updatePlacesCommand("placesCmd_open:tab");
- updatePlacesCommand("placesCmd_new:folder");
- updatePlacesCommand("placesCmd_new:bookmark");
- updatePlacesCommand("placesCmd_new:livemark");
- updatePlacesCommand("placesCmd_new:separator");
- updatePlacesCommand("placesCmd_show:info");
- updatePlacesCommand("placesCmd_moveBookmarks");
- updatePlacesCommand("placesCmd_reload");
- updatePlacesCommand("placesCmd_sortBy:name");
- updatePlacesCommand("placesCmd_openParentFolder");
- updatePlacesCommand("placesCmd_cut");
- updatePlacesCommand("placesCmd_copy");
- updatePlacesCommand("placesCmd_paste");
- updatePlacesCommand("placesCmd_delete");
-}
-
-function doGetPlacesControllerForCommand(aCommand)
-{
- // A context menu may be built for non-focusable views. Thus, we first try
- // to look for a view associated with document.popupNode
- let popupNode;
- try {
- popupNode = document.popupNode;
- } catch (e) {
- // The document went away (bug 797307).
- return null;
- }
- if (popupNode) {
- let view = PlacesUIUtils.getViewForNode(popupNode);
- if (view && view._contextMenuShown)
- return view.controllers.getControllerForCommand(aCommand);
- }
-
- // When we're not building a context menu, only focusable views
- // are possible. Thus, we can safely use the command dispatcher.
- let controller = top.document.commandDispatcher
- .getControllerForCommand(aCommand);
- if (controller)
- return controller;
-
- return null;
-}
-
-function goDoPlacesCommand(aCommand)
-{
- let controller = doGetPlacesControllerForCommand(aCommand);
- if (controller && controller.isCommandEnabled(aCommand))
- controller.doCommand(aCommand);
-}
-
diff --git a/components/places/content/downloadsViewOverlay.xul b/components/places/content/downloadsViewOverlay.xul
deleted file mode 100644
index 8e2d775..0000000
--- a/components/places/content/downloadsViewOverlay.xul
+++ /dev/null
@@ -1,47 +0,0 @@
-<!-- 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/. -->
-
-<?xul-overlay href="chrome://browser/content/downloads/allDownloadsViewOverlay.xul"?>
-
-<!DOCTYPE overlay [
-<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
-%downloadsDTD;
-]>
-
-<overlay id="downloadsViewOverlay"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <script type="application/javascript"><![CDATA[
- const DOWNLOADS_QUERY = "place:transition=" +
- Components.interfaces.nsINavHistoryService.TRANSITION_DOWNLOAD +
- "&sort=" +
- Components.interfaces.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING;
-
- ContentArea.setContentViewForQueryString(DOWNLOADS_QUERY,
- function() new DownloadsPlacesView(document.getElementById("downloadsRichListBox"), false),
- { showDetailsPane: false,
- toolbarSet: "back-button, forward-button, organizeButton, clearDownloadsButton, libraryToolbarSpacer, searchFilter" });
- ]]></script>
-
- <window id="places">
- <commandset id="downloadCommands"/>
- <menupopup id="downloadsContextMenu"/>
- </window>
-
- <deck id="placesViewsDeck">
- <richlistbox id="downloadsRichListBox"/>
- </deck>
-
- <toolbar id="placesToolbar">
- <toolbarbutton id="clearDownloadsButton"
-#ifdef XP_MACOSX
- class="tabbable"
-#endif
- insertbefore="libraryToolbarSpacer"
- label="&clearDownloadsButton.label;"
- command="downloadsCmd_clearDownloads"
- tooltiptext="&clearDownloadsButton.tooltip;"/>
- </toolbar>
-
-</overlay>
diff --git a/components/places/content/editBookmarkOverlay.js b/components/places/content/editBookmarkOverlay.js
deleted file mode 100644
index 69d7d32..0000000
--- a/components/places/content/editBookmarkOverlay.js
+++ /dev/null
@@ -1,1063 +0,0 @@
-/* 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/. */
-
-const LAST_USED_ANNO = "bookmarkPropertiesDialog/folderLastUsed";
-const MAX_FOLDER_ITEM_IN_MENU_LIST = 5;
-
-var gEditItemOverlay = {
- _uri: null,
- _itemId: -1,
- _itemIds: [],
- _uris: [],
- _tags: [],
- _allTags: [],
- _keyword: null,
- _multiEdit: false,
- _itemType: -1,
- _readOnly: false,
- _hiddenRows: [],
- _onPanelReady: false,
- _observersAdded: false,
- _staticFoldersListBuilt: false,
- _initialized: false,
- _titleOverride: "",
-
- // the first field which was edited after this panel was initialized for
- // a certain item
- _firstEditedField: "",
-
- get itemId() {
- return this._itemId;
- },
-
- get uri() {
- return this._uri;
- },
-
- get multiEdit() {
- return this._multiEdit;
- },
-
- /**
- * Determines the initial data for the item edited or added by this dialog
- */
- _determineInfo: function EIO__determineInfo(aInfo) {
- // hidden rows
- if (aInfo && aInfo.hiddenRows)
- this._hiddenRows = aInfo.hiddenRows;
- else
- this._hiddenRows.splice(0, this._hiddenRows.length);
- // force-read-only
- this._readOnly = aInfo && aInfo.forceReadOnly;
- this._titleOverride = aInfo && aInfo.titleOverride ? aInfo.titleOverride
- : "";
- this._onPanelReady = aInfo && aInfo.onPanelReady;
- },
-
- _showHideRows: function EIO__showHideRows() {
- var isBookmark = this._itemId != -1 &&
- this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK;
- var isQuery = false;
- if (this._uri)
- isQuery = this._uri.schemeIs("place");
-
- this._element("nameRow").collapsed = this._hiddenRows.indexOf("name") != -1;
- this._element("folderRow").collapsed =
- this._hiddenRows.indexOf("folderPicker") != -1 || this._readOnly;
- this._element("tagsRow").collapsed = !this._uri ||
- this._hiddenRows.indexOf("tags") != -1 || isQuery;
- // Collapse the tag selector if the item does not accept tags.
- if (!this._element("tagsSelectorRow").collapsed &&
- this._element("tagsRow").collapsed)
- this.toggleTagsSelector();
- this._element("descriptionRow").collapsed =
- this._hiddenRows.indexOf("description") != -1 || this._readOnly;
- this._element("keywordRow").collapsed = !isBookmark || this._readOnly ||
- this._hiddenRows.indexOf("keyword") != -1 || isQuery;
- this._element("locationRow").collapsed = !(this._uri && !isQuery) ||
- this._hiddenRows.indexOf("location") != -1;
- this._element("loadInSidebarCheckbox").collapsed = !isBookmark || isQuery ||
- this._readOnly || this._hiddenRows.indexOf("loadInSidebar") != -1;
- this._element("feedLocationRow").collapsed = !this._isLivemark ||
- this._hiddenRows.indexOf("feedLocation") != -1;
- this._element("siteLocationRow").collapsed = !this._isLivemark ||
- this._hiddenRows.indexOf("siteLocation") != -1;
- this._element("selectionCount").hidden = !this._multiEdit;
- },
-
- /**
- * Initialize the panel
- * @param aFor
- * Either a places-itemId (of a bookmark, folder or a live bookmark),
- * an array of itemIds (used for bulk tagging), or a URI object (in
- * which case, the panel would be initialized in read-only mode).
- * @param [optional] aInfo
- * JS object which stores additional info for the panel
- * initialization. The following properties may bet set:
- * * hiddenRows (Strings array): list of rows to be hidden regardless
- * of the item edited. Possible values: "title", "location",
- * "description", "keyword", "loadInSidebar", "feedLocation",
- * "siteLocation", folderPicker"
- * * forceReadOnly - set this flag to initialize the panel to its
- * read-only (view) mode even if the given item is editable.
- */
- initPanel: function EIO_initPanel(aFor, aInfo) {
- // For sanity ensure that the implementer has uninited the panel before
- // trying to init it again, or we could end up leaking due to observers.
- if (this._initialized)
- this.uninitPanel(false);
-
- var aItemIdList;
- if (Array.isArray(aFor)) {
- aItemIdList = aFor;
- aFor = aItemIdList[0];
- }
- else if (this._multiEdit) {
- this._multiEdit = false;
- this._tags = [];
- this._uris = [];
- this._allTags = [];
- this._itemIds = [];
- this._element("selectionCount").hidden = true;
- }
-
- this._folderMenuList = this._element("folderMenuList");
- this._folderTree = this._element("folderTree");
-
- this._determineInfo(aInfo);
- if (aFor instanceof Ci.nsIURI) {
- this._itemId = -1;
- this._uri = aFor;
- this._readOnly = true;
- }
- else {
- this._itemId = aFor;
- // We can't store information on invalid itemIds.
- this._readOnly = this._readOnly || this._itemId == -1;
-
- var containerId = PlacesUtils.bookmarks.getFolderIdForItem(this._itemId);
- this._itemType = PlacesUtils.bookmarks.getItemType(this._itemId);
- if (this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) {
- this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
- this._keyword = PlacesUtils.bookmarks.getKeywordForBookmark(this._itemId);
- this._initTextField("keywordField", this._keyword);
- this._element("loadInSidebarCheckbox").checked =
- PlacesUtils.annotations.itemHasAnnotation(this._itemId,
- PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO);
- }
- else {
- this._uri = null;
- this._isLivemark = false;
- PlacesUtils.livemarks.getLivemark({id: this._itemId })
- .then(aLivemark => {
- this._isLivemark = true;
- this._initTextField("feedLocationField", aLivemark.feedURI.spec, true);
- this._initTextField("siteLocationField", aLivemark.siteURI ? aLivemark.siteURI.spec : "", true);
- this._showHideRows();
- }, () => undefined);
- }
-
- // folder picker
- this._initFolderMenuList(containerId);
-
- // description field
- this._initTextField("descriptionField",
- PlacesUIUtils.getItemDescription(this._itemId));
- }
-
- if (this._itemId == -1 ||
- this._itemType == Ci.nsINavBookmarksService.TYPE_BOOKMARK) {
- this._isLivemark = false;
-
- this._initTextField("locationField", this._uri.spec);
- if (!aItemIdList) {
- var tags = PlacesUtils.tagging.getTagsForURI(this._uri).join(", ");
- this._initTextField("tagsField", tags, false);
- }
- else {
- this._multiEdit = true;
- this._allTags = [];
- this._itemIds = aItemIdList;
- for (var i = 0; i < aItemIdList.length; i++) {
- if (aItemIdList[i] instanceof Ci.nsIURI) {
- this._uris[i] = aItemIdList[i];
- this._itemIds[i] = -1;
- }
- else
- this._uris[i] = PlacesUtils.bookmarks.getBookmarkURI(this._itemIds[i]);
- this._tags[i] = PlacesUtils.tagging.getTagsForURI(this._uris[i]);
- }
- this._allTags = this._getCommonTags();
- this._initTextField("tagsField", this._allTags.join(", "), false);
- this._element("itemsCountText").value =
- PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
- this._itemIds.length,
- [this._itemIds.length]);
- }
-
- // tags selector
- this._rebuildTagsSelectorList();
- }
-
- // name picker
- this._initNamePicker();
-
- this._showHideRows();
-
- // observe changes
- if (!this._observersAdded) {
- // Single bookmarks observe any change. History entries and multiEdit
- // observe only tags changes, through bookmarks.
- if (this._itemId != -1 || this._uri || this._multiEdit)
- PlacesUtils.bookmarks.addObserver(this, false);
-
- this._element("namePicker").addEventListener("blur", this);
- this._element("locationField").addEventListener("blur", this);
- this._element("tagsField").addEventListener("blur", this);
- this._element("keywordField").addEventListener("blur", this);
- this._element("descriptionField").addEventListener("blur", this);
- window.addEventListener("unload", this, false);
- this._observersAdded = true;
- }
-
- let focusElement = () => {
- this._initialized = true;
- };
-
- if (this._onPanelReady) {
- this._onPanelReady(focusElement);
- } else {
- focusElement();
- }
- },
-
- /**
- * Finds tags that are in common among this._tags entries that track tags
- * for each selected uri.
- * The tags arrays should be kept up-to-date for this to work properly.
- *
- * @return array of common tags for the selected uris.
- */
- _getCommonTags: function() {
- return this._tags[0].filter(
- function (aTag) this._tags.every(
- function (aTags) aTags.indexOf(aTag) != -1
- ), this
- );
- },
-
- _initTextField: function(aTextFieldId, aValue, aReadOnly) {
- var field = this._element(aTextFieldId);
- field.readOnly = aReadOnly !== undefined ? aReadOnly : this._readOnly;
-
- if (field.value != aValue) {
- field.value = aValue;
- this._editorTransactionManagerClear(field);
- }
- },
-
- /**
- * Appends a menu-item representing a bookmarks folder to a menu-popup.
- * @param aMenupopup
- * The popup to which the menu-item should be added.
- * @param aFolderId
- * The identifier of the bookmarks folder.
- * @return the new menu item.
- */
- _appendFolderItemToMenupopup:
- function EIO__appendFolderItemToMenuList(aMenupopup, aFolderId) {
- // First make sure the folders-separator is visible
- this._element("foldersSeparator").hidden = false;
-
- var folderMenuItem = document.createElement("menuitem");
- var folderTitle = PlacesUtils.bookmarks.getItemTitle(aFolderId)
- folderMenuItem.folderId = aFolderId;
- folderMenuItem.setAttribute("label", folderTitle);
- folderMenuItem.className = "menuitem-iconic folder-icon";
- aMenupopup.appendChild(folderMenuItem);
- return folderMenuItem;
- },
-
- _initFolderMenuList: function EIO__initFolderMenuList(aSelectedFolder) {
- // clean up first
- var menupopup = this._folderMenuList.menupopup;
- while (menupopup.childNodes.length > 6)
- menupopup.removeChild(menupopup.lastChild);
-
- const bms = PlacesUtils.bookmarks;
- const annos = PlacesUtils.annotations;
-
- // Build the static list
- var unfiledItem = this._element("unfiledRootItem");
- if (!this._staticFoldersListBuilt) {
- unfiledItem.label = bms.getItemTitle(PlacesUtils.unfiledBookmarksFolderId);
- unfiledItem.folderId = PlacesUtils.unfiledBookmarksFolderId;
- var bmMenuItem = this._element("bmRootItem");
- bmMenuItem.label = bms.getItemTitle(PlacesUtils.bookmarksMenuFolderId);
- bmMenuItem.folderId = PlacesUtils.bookmarksMenuFolderId;
- var toolbarItem = this._element("toolbarFolderItem");
- toolbarItem.label = bms.getItemTitle(PlacesUtils.toolbarFolderId);
- toolbarItem.folderId = PlacesUtils.toolbarFolderId;
- this._staticFoldersListBuilt = true;
- }
-
- // List of recently used folders:
- var folderIds = annos.getItemsWithAnnotation(LAST_USED_ANNO);
-
- /**
- * The value of the LAST_USED_ANNO annotation is the time (in the form of
- * Date.getTime) at which the folder has been last used.
- *
- * First we build the annotated folders array, each item has both the
- * folder identifier and the time at which it was last-used by this dialog
- * set. Then we sort it descendingly based on the time field.
- */
- this._recentFolders = [];
- for (var i = 0; i < folderIds.length; i++) {
- var lastUsed = annos.getItemAnnotation(folderIds[i], LAST_USED_ANNO);
- this._recentFolders.push({ folderId: folderIds[i], lastUsed: lastUsed });
- }
- this._recentFolders.sort(function(a, b) {
- if (b.lastUsed < a.lastUsed)
- return -1;
- if (b.lastUsed > a.lastUsed)
- return 1;
- return 0;
- });
-
- var numberOfItems = Math.min(MAX_FOLDER_ITEM_IN_MENU_LIST,
- this._recentFolders.length);
- for (var i = 0; i < numberOfItems; i++) {
- this._appendFolderItemToMenupopup(menupopup,
- this._recentFolders[i].folderId);
- }
-
- var defaultItem = this._getFolderMenuItem(aSelectedFolder);
- this._folderMenuList.selectedItem = defaultItem;
-
- // Set a selectedIndex attribute to show special icons
- this._folderMenuList.setAttribute("selectedIndex",
- this._folderMenuList.selectedIndex);
-
- // Hide the folders-separator if no folder is annotated as recently-used
- this._element("foldersSeparator").hidden = (menupopup.childNodes.length <= 6);
- this._folderMenuList.disabled = this._readOnly;
- },
-
- QueryInterface: function EIO_QueryInterface(aIID) {
- if (aIID.equals(Ci.nsIDOMEventListener) ||
- aIID.equals(Ci.nsINavBookmarkObserver) ||
- aIID.equals(Ci.nsISupports))
- return this;
-
- throw Cr.NS_ERROR_NO_INTERFACE;
- },
-
- _element: function EIO__element(aID) {
- return document.getElementById("editBMPanel_" + aID);
- },
-
- _editorTransactionManagerClear: function EIO__editorTransactionManagerClear(aItem) {
- // Clear the editor's undo stack
- let transactionManager;
- try {
- transactionManager = aItem.editor.transactionManager;
- } catch (e) {
- // When retrieving the transaction manager, editor may be null resulting
- // in a TypeError. Additionally, the transaction manager may not
- // exist yet, which causes access to it to throw NS_ERROR_FAILURE.
- // In either event, the transaction manager doesn't exist it, so we
- // don't need to worry about clearing it.
- if (!(e instanceof TypeError) && e.result != Cr.NS_ERROR_FAILURE) {
- throw e;
- }
- }
- if (transactionManager) {
- transactionManager.clear();
- }
- },
-
- _getItemStaticTitle: function EIO__getItemStaticTitle() {
- if (this._titleOverride)
- return this._titleOverride;
-
- let title = "";
- if (this._itemId == -1) {
- title = PlacesUtils.history.getPageTitle(this._uri);
- }
- else {
- title = PlacesUtils.bookmarks.getItemTitle(this._itemId);
- }
- return title;
- },
-
- _initNamePicker: function EIO_initNamePicker() {
- var namePicker = this._element("namePicker");
- namePicker.value = this._getItemStaticTitle();
- namePicker.readOnly = this._readOnly;
- this._editorTransactionManagerClear(namePicker);
- },
-
- uninitPanel: function EIO_uninitPanel(aHideCollapsibleElements) {
- if (aHideCollapsibleElements) {
- // hide the folder tree if it was previously visible
- var folderTreeRow = this._element("folderTreeRow");
- if (!folderTreeRow.collapsed)
- this.toggleFolderTreeVisibility();
-
- // hide the tag selector if it was previously visible
- var tagsSelectorRow = this._element("tagsSelectorRow");
- if (!tagsSelectorRow.collapsed)
- this.toggleTagsSelector();
- }
-
- if (this._observersAdded) {
- if (this._itemId != -1 || this._uri || this._multiEdit)
- PlacesUtils.bookmarks.removeObserver(this);
-
- this._element("namePicker").removeEventListener("blur", this);
- this._element("locationField").removeEventListener("blur", this);
- this._element("tagsField").removeEventListener("blur", this);
- this._element("keywordField").removeEventListener("blur", this);
- this._element("descriptionField").removeEventListener("blur", this);
-
- this._observersAdded = false;
- }
-
- this._itemId = -1;
- this._uri = null;
- this._uris = [];
- this._tags = [];
- this._allTags = [];
- this._itemIds = [];
- this._multiEdit = false;
- this._firstEditedField = "";
- this._initialized = false;
- this._titleOverride = "";
- this._readOnly = false;
- },
-
- onTagsFieldBlur: function EIO_onTagsFieldBlur() {
- if (this._updateTags()) // if anything has changed
- this._mayUpdateFirstEditField("tagsField");
- },
-
- _updateTags: function EIO__updateTags() {
- if (this._multiEdit)
- return this._updateMultipleTagsForItems();
- return this._updateSingleTagForItem();
- },
-
- _updateSingleTagForItem: function EIO__updateSingleTagForItem() {
- var currentTags = PlacesUtils.tagging.getTagsForURI(this._uri);
- var tags = this._getTagsArrayFromTagField();
- if (tags.length > 0 || currentTags.length > 0) {
- var tagsToRemove = [];
- var tagsToAdd = [];
- var txns = [];
- for (var i = 0; i < currentTags.length; i++) {
- if (tags.indexOf(currentTags[i]) == -1)
- tagsToRemove.push(currentTags[i]);
- }
- for (var i = 0; i < tags.length; i++) {
- if (currentTags.indexOf(tags[i]) == -1)
- tagsToAdd.push(tags[i]);
- }
-
- if (tagsToRemove.length > 0) {
- let untagTxn = new PlacesUntagURITransaction(this._uri, tagsToRemove);
- txns.push(untagTxn);
- }
- if (tagsToAdd.length > 0) {
- let tagTxn = new PlacesTagURITransaction(this._uri, tagsToAdd);
- txns.push(tagTxn);
- }
-
- if (txns.length > 0) {
- let aggregate = new PlacesAggregatedTransaction("Update tags", txns);
- PlacesUtils.transactionManager.doTransaction(aggregate);
-
- // Ensure the tagsField is in sync, clean it up from empty tags
- var tags = PlacesUtils.tagging.getTagsForURI(this._uri).join(", ");
- this._initTextField("tagsField", tags, false);
- return true;
- }
- }
- return false;
- },
-
- /**
- * Stores the first-edit field for this dialog, if the passed-in field
- * is indeed the first edited field
- * @param aNewField
- * the id of the field that may be set (without the "editBMPanel_"
- * prefix)
- */
- _mayUpdateFirstEditField: function EIO__mayUpdateFirstEditField(aNewField) {
- // * The first-edit-field behavior is not applied in the multi-edit case
- // * if this._firstEditedField is already set, this is not the first field,
- // so there's nothing to do
- if (this._multiEdit || this._firstEditedField)
- return;
-
- this._firstEditedField = aNewField;
-
- // set the pref
- var prefs = Cc["@mozilla.org/preferences-service;1"].
- getService(Ci.nsIPrefBranch);
- prefs.setCharPref("browser.bookmarks.editDialog.firstEditField", aNewField);
- },
-
- _updateMultipleTagsForItems: function EIO__updateMultipleTagsForItems() {
- var tags = this._getTagsArrayFromTagField();
- if (tags.length > 0 || this._allTags.length > 0) {
- var tagsToRemove = [];
- var tagsToAdd = [];
- var txns = [];
- for (var i = 0; i < this._allTags.length; i++) {
- if (tags.indexOf(this._allTags[i]) == -1)
- tagsToRemove.push(this._allTags[i]);
- }
- for (var i = 0; i < this._tags.length; i++) {
- tagsToAdd[i] = [];
- for (var j = 0; j < tags.length; j++) {
- if (this._tags[i].indexOf(tags[j]) == -1)
- tagsToAdd[i].push(tags[j]);
- }
- }
-
- if (tagsToAdd.length > 0) {
- for (let i = 0; i < this._uris.length; i++) {
- if (tagsToAdd[i].length > 0) {
- let tagTxn = new PlacesTagURITransaction(this._uris[i],
- tagsToAdd[i]);
- txns.push(tagTxn);
- }
- }
- }
- if (tagsToRemove.length > 0) {
- for (let i = 0; i < this._uris.length; i++) {
- let untagTxn = new PlacesUntagURITransaction(this._uris[i],
- tagsToRemove);
- txns.push(untagTxn);
- }
- }
-
- if (txns.length > 0) {
- let aggregate = new PlacesAggregatedTransaction("Update tags", txns);
- PlacesUtils.transactionManager.doTransaction(aggregate);
-
- this._allTags = tags;
- this._tags = [];
- for (let i = 0; i < this._uris.length; i++) {
- this._tags[i] = PlacesUtils.tagging.getTagsForURI(this._uris[i]);
- }
-
- // Ensure the tagsField is in sync, clean it up from empty tags
- this._initTextField("tagsField", tags, false);
- return true;
- }
- }
- return false;
- },
-
- onNamePickerBlur: function EIO_onNamePickerBlur() {
- if (this._itemId == -1)
- return;
-
- var namePicker = this._element("namePicker")
-
- // Here we update either the item title or its cached static title
- var newTitle = namePicker.value;
- if (!newTitle &&
- PlacesUtils.bookmarks.getFolderIdForItem(this._itemId) == PlacesUtils.tagsFolderId) {
- // We don't allow setting an empty title for a tag, restore the old one.
- this._initNamePicker();
- }
- else if (this._getItemStaticTitle() != newTitle) {
- this._mayUpdateFirstEditField("namePicker");
- let txn = new PlacesEditItemTitleTransaction(this._itemId, newTitle);
- PlacesUtils.transactionManager.doTransaction(txn);
- }
- },
-
- onDescriptionFieldBlur: function EIO_onDescriptionFieldBlur() {
- var description = this._element("descriptionField").value;
- if (description != PlacesUIUtils.getItemDescription(this._itemId)) {
- var annoObj = { name : PlacesUIUtils.DESCRIPTION_ANNO,
- type : Ci.nsIAnnotationService.TYPE_STRING,
- flags : 0,
- value : description,
- expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
- var txn = new PlacesSetItemAnnotationTransaction(this._itemId, annoObj);
- PlacesUtils.transactionManager.doTransaction(txn);
- }
- },
-
- onLocationFieldBlur: function EIO_onLocationFieldBlur() {
- var uri;
- try {
- uri = PlacesUIUtils.createFixedURI(this._element("locationField").value);
- }
- catch(ex) { return; }
-
- if (!this._uri.equals(uri)) {
- var txn = new PlacesEditBookmarkURITransaction(this._itemId, uri);
- PlacesUtils.transactionManager.doTransaction(txn);
- this._uri = uri;
- }
- },
-
- onKeywordFieldBlur: function EIO_onKeywordFieldBlur() {
- let oldKeyword = this._keyword;
- let keyword = this._keyword = this._element("keywordField").value;
- if (keyword != oldKeyword) {
- let txn = new PlacesEditBookmarkKeywordTransaction(this._itemId,
- keyword,
- null,
- oldKeyword);
- PlacesUtils.transactionManager.doTransaction(txn);
- }
- },
-
- onLoadInSidebarCheckboxCommand:
- function EIO_onLoadInSidebarCheckboxCommand() {
- let annoObj = { name : PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO };
- if (this._element("loadInSidebarCheckbox").checked)
- annoObj.value = true;
- let txn = new PlacesSetItemAnnotationTransaction(this._itemId, annoObj);
- PlacesUtils.transactionManager.doTransaction(txn);
- },
-
- toggleFolderTreeVisibility: function EIO_toggleFolderTreeVisibility() {
- var expander = this._element("foldersExpander");
- var folderTreeRow = this._element("folderTreeRow");
- if (!folderTreeRow.collapsed) {
- expander.className = "expander-down";
- expander.setAttribute("tooltiptext",
- expander.getAttribute("tooltiptextdown"));
- folderTreeRow.collapsed = true;
- this._element("chooseFolderSeparator").hidden =
- this._element("chooseFolderMenuItem").hidden = false;
- }
- else {
- expander.className = "expander-up"
- expander.setAttribute("tooltiptext",
- expander.getAttribute("tooltiptextup"));
- folderTreeRow.collapsed = false;
-
- // XXXmano: Ideally we would only do this once, but for some odd reason,
- // the editable mode set on this tree, together with its collapsed state
- // breaks the view.
- const FOLDER_TREE_PLACE_URI =
- "place:excludeItems=1&excludeQueries=1&excludeReadOnlyFolders=1&folder=" +
- PlacesUIUtils.allBookmarksFolderId;
- this._folderTree.place = FOLDER_TREE_PLACE_URI;
-
- this._element("chooseFolderSeparator").hidden =
- this._element("chooseFolderMenuItem").hidden = true;
- var currentFolder = this._getFolderIdFromMenuList();
- this._folderTree.selectItems([currentFolder]);
- this._folderTree.focus();
- }
- },
-
- _getFolderIdFromMenuList:
- function EIO__getFolderIdFromMenuList() {
- var selectedItem = this._folderMenuList.selectedItem;
- NS_ASSERT("folderId" in selectedItem,
- "Invalid menuitem in the folders-menulist");
- return selectedItem.folderId;
- },
-
- /**
- * Get the corresponding menu-item in the folder-menu-list for a bookmarks
- * folder if such an item exists. Otherwise, this creates a menu-item for the
- * folder. If the items-count limit (see MAX_FOLDERS_IN_MENU_LIST) is reached,
- * the new item replaces the last menu-item.
- * @param aFolderId
- * The identifier of the bookmarks folder.
- */
- _getFolderMenuItem:
- function EIO__getFolderMenuItem(aFolderId) {
- var menupopup = this._folderMenuList.menupopup;
-
- for (let i = 0; i < menupopup.childNodes.length; i++) {
- if ("folderId" in menupopup.childNodes[i] &&
- menupopup.childNodes[i].folderId == aFolderId)
- return menupopup.childNodes[i];
- }
-
- // 3 special folders + separator + folder-items-count limit
- if (menupopup.childNodes.length == 4 + MAX_FOLDER_ITEM_IN_MENU_LIST)
- menupopup.removeChild(menupopup.lastChild);
-
- return this._appendFolderItemToMenupopup(menupopup, aFolderId);
- },
-
- onFolderMenuListCommand: function EIO_onFolderMenuListCommand(aEvent) {
- // Set a selectedIndex attribute to show special icons
- this._folderMenuList.setAttribute("selectedIndex",
- this._folderMenuList.selectedIndex);
-
- if (aEvent.target.id == "editBMPanel_chooseFolderMenuItem") {
- // reset the selection back to where it was and expand the tree
- // (this menu-item is hidden when the tree is already visible
- var container = PlacesUtils.bookmarks.getFolderIdForItem(this._itemId);
- var item = this._getFolderMenuItem(container);
- this._folderMenuList.selectedItem = item;
- // XXXmano HACK: setTimeout 100, otherwise focus goes back to the
- // menulist right away
- setTimeout(function(self) self.toggleFolderTreeVisibility(), 100, this);
- return;
- }
-
- // Move the item
- var container = this._getFolderIdFromMenuList();
- if (PlacesUtils.bookmarks.getFolderIdForItem(this._itemId) != container) {
- var txn = new PlacesMoveItemTransaction(this._itemId,
- container,
- PlacesUtils.bookmarks.DEFAULT_INDEX);
- PlacesUtils.transactionManager.doTransaction(txn);
-
- // Mark the containing folder as recently-used if it isn't in the
- // static list
- if (container != PlacesUtils.unfiledBookmarksFolderId &&
- container != PlacesUtils.toolbarFolderId &&
- container != PlacesUtils.bookmarksMenuFolderId)
- this._markFolderAsRecentlyUsed(container);
- }
-
- // Update folder-tree selection
- var folderTreeRow = this._element("folderTreeRow");
- if (!folderTreeRow.collapsed) {
- var selectedNode = this._folderTree.selectedNode;
- if (!selectedNode ||
- PlacesUtils.getConcreteItemId(selectedNode) != container)
- this._folderTree.selectItems([container]);
- }
- },
-
- onFolderTreeSelect: function EIO_onFolderTreeSelect() {
- var selectedNode = this._folderTree.selectedNode;
-
- // Disable the "New Folder" button if we cannot create a new folder
- this._element("newFolderButton")
- .disabled = !this._folderTree.insertionPoint || !selectedNode;
-
- if (!selectedNode)
- return;
-
- var folderId = PlacesUtils.getConcreteItemId(selectedNode);
- if (this._getFolderIdFromMenuList() == folderId)
- return;
-
- var folderItem = this._getFolderMenuItem(folderId);
- this._folderMenuList.selectedItem = folderItem;
- folderItem.doCommand();
- },
-
- _markFolderAsRecentlyUsed:
- function EIO__markFolderAsRecentlyUsed(aFolderId) {
- var txns = [];
-
- // Expire old unused recent folders
- var anno = this._getLastUsedAnnotationObject(false);
- while (this._recentFolders.length > MAX_FOLDER_ITEM_IN_MENU_LIST) {
- var folderId = this._recentFolders.pop().folderId;
- let annoTxn = new PlacesSetItemAnnotationTransaction(folderId, anno);
- txns.push(annoTxn);
- }
-
- // Mark folder as recently used
- anno = this._getLastUsedAnnotationObject(true);
- let annoTxn = new PlacesSetItemAnnotationTransaction(aFolderId, anno);
- txns.push(annoTxn);
-
- let aggregate = new PlacesAggregatedTransaction("Update last used folders", txns);
- PlacesUtils.transactionManager.doTransaction(aggregate);
- },
-
- /**
- * Returns an object which could then be used to set/unset the
- * LAST_USED_ANNO annotation for a folder.
- *
- * @param aLastUsed
- * Whether to set or unset the LAST_USED_ANNO annotation.
- * @returns an object representing the annotation which could then be used
- * with the transaction manager.
- */
- _getLastUsedAnnotationObject:
- function EIO__getLastUsedAnnotationObject(aLastUsed) {
- var anno = { name: LAST_USED_ANNO,
- type: Ci.nsIAnnotationService.TYPE_INT32,
- flags: 0,
- value: aLastUsed ? new Date().getTime() : null,
- expires: Ci.nsIAnnotationService.EXPIRE_NEVER };
-
- return anno;
- },
-
- _rebuildTagsSelectorList: function EIO__rebuildTagsSelectorList() {
- var tagsSelector = this._element("tagsSelector");
- var tagsSelectorRow = this._element("tagsSelectorRow");
- if (tagsSelectorRow.collapsed)
- return;
-
- // Save the current scroll position and restore it after the rebuild.
- let firstIndex = tagsSelector.getIndexOfFirstVisibleRow();
- let selectedIndex = tagsSelector.selectedIndex;
- let selectedTag = selectedIndex >= 0 ? tagsSelector.selectedItem.label
- : null;
-
- while (tagsSelector.hasChildNodes())
- tagsSelector.removeChild(tagsSelector.lastChild);
-
- var tagsInField = this._getTagsArrayFromTagField();
- var allTags = PlacesUtils.tagging.allTags;
- for (var i = 0; i < allTags.length; i++) {
- var tag = allTags[i];
- var elt = document.createElement("listitem");
- elt.setAttribute("type", "checkbox");
- elt.setAttribute("label", tag);
- if (tagsInField.indexOf(tag) != -1)
- elt.setAttribute("checked", "true");
- tagsSelector.appendChild(elt);
- if (selectedTag === tag)
- selectedIndex = tagsSelector.getIndexOfItem(elt);
- }
-
- // Restore position.
- // The listbox allows to scroll only if the required offset doesn't
- // overflow its capacity, thus need to adjust the index for removals.
- firstIndex =
- Math.min(firstIndex,
- tagsSelector.itemCount - tagsSelector.getNumberOfVisibleRows());
- tagsSelector.scrollToIndex(firstIndex);
- if (selectedIndex >= 0 && tagsSelector.itemCount > 0) {
- selectedIndex = Math.min(selectedIndex, tagsSelector.itemCount - 1);
- tagsSelector.selectedIndex = selectedIndex;
- tagsSelector.ensureIndexIsVisible(selectedIndex);
- }
- },
-
- toggleTagsSelector: function EIO_toggleTagsSelector() {
- var tagsSelector = this._element("tagsSelector");
- var tagsSelectorRow = this._element("tagsSelectorRow");
- var expander = this._element("tagsSelectorExpander");
- if (tagsSelectorRow.collapsed) {
- expander.className = "expander-up";
- expander.setAttribute("tooltiptext",
- expander.getAttribute("tooltiptextup"));
- tagsSelectorRow.collapsed = false;
- this._rebuildTagsSelectorList();
-
- // This is a no-op if we've added the listener.
- tagsSelector.addEventListener("CheckboxStateChange", this, false);
- }
- else {
- expander.className = "expander-down";
- expander.setAttribute("tooltiptext",
- expander.getAttribute("tooltiptextdown"));
- tagsSelectorRow.collapsed = true;
- }
- },
-
- /**
- * Splits "tagsField" element value, returning an array of valid tag strings.
- *
- * @return Array of tag strings found in the field value.
- */
- _getTagsArrayFromTagField: function EIO__getTagsArrayFromTagField() {
- let tags = this._element("tagsField").value;
- return tags.trim()
- .split(/\s*,\s*/) // Split on commas and remove spaces.
- .filter(function (tag) tag.length > 0); // Kill empty tags.
- },
-
- newFolder: function EIO_newFolder() {
- var ip = this._folderTree.insertionPoint;
-
- // default to the bookmarks menu folder
- if (!ip || ip.itemId == PlacesUIUtils.allBookmarksFolderId) {
- ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
- PlacesUtils.bookmarks.DEFAULT_INDEX,
- Ci.nsITreeView.DROP_ON);
- }
-
- // XXXmano: add a separate "New Folder" string at some point...
- var defaultLabel = this._element("newFolderButton").label;
- var txn = new PlacesCreateFolderTransaction(defaultLabel, ip.itemId, ip.index);
- PlacesUtils.transactionManager.doTransaction(txn);
- this._folderTree.focus();
- this._folderTree.selectItems([this._lastNewItem]);
- this._folderTree.startEditing(this._folderTree.view.selection.currentIndex,
- this._folderTree.columns.getFirstColumn());
- },
-
- // nsIDOMEventListener
- handleEvent: function EIO_nsIDOMEventListener(aEvent) {
- switch (aEvent.type) {
- case "CheckboxStateChange":
- // Update the tags field when items are checked/unchecked in the listbox
- var tags = this._getTagsArrayFromTagField();
-
- if (aEvent.target.checked) {
- if (tags.indexOf(aEvent.target.label) == -1)
- tags.push(aEvent.target.label);
- }
- else {
- var indexOfItem = tags.indexOf(aEvent.target.label);
- if (indexOfItem != -1)
- tags.splice(indexOfItem, 1);
- }
- this._element("tagsField").value = tags.join(", ");
- this._updateTags();
- break;
- case "blur":
- let replaceFn = (str, firstLetter) => firstLetter.toUpperCase();
- let nodeName = aEvent.target.id.replace(/editBMPanel_(\w)/, replaceFn);
- this["on" + nodeName + "Blur"]();
- break;
- case "unload":
- this.uninitPanel(false);
- break;
- }
- },
-
- // nsINavBookmarkObserver
- onItemChanged: function EIO_onItemChanged(aItemId, aProperty,
- aIsAnnotationProperty, aValue,
- aLastModified, aItemType) {
- if (aProperty == "tags") {
- // Tags case is special, since they should be updated if either:
- // - the notification is for the edited bookmark
- // - the notification is for the edited history entry
- // - the notification is for one of edited uris
- let shouldUpdateTagsField = this._itemId == aItemId;
- if (this._itemId == -1 || this._multiEdit) {
- // Check if the changed uri is part of the modified ones.
- let changedURI = PlacesUtils.bookmarks.getBookmarkURI(aItemId);
- let uris = this._multiEdit ? this._uris : [this._uri];
- uris.forEach(function (aURI, aIndex) {
- if (aURI.equals(changedURI)) {
- shouldUpdateTagsField = true;
- if (this._multiEdit) {
- this._tags[aIndex] = PlacesUtils.tagging.getTagsForURI(this._uris[aIndex]);
- }
- }
- }, this);
- }
-
- if (shouldUpdateTagsField) {
- if (this._multiEdit) {
- this._allTags = this._getCommonTags();
- this._initTextField("tagsField", this._allTags.join(", "), false);
- }
- else {
- let tags = PlacesUtils.tagging.getTagsForURI(this._uri).join(", ");
- this._initTextField("tagsField", tags, false);
- }
- }
-
- // Any tags change should be reflected in the tags selector.
- this._rebuildTagsSelectorList();
- return;
- }
-
- if (this._itemId != aItemId) {
- if (aProperty == "title") {
- // If the title of a folder which is listed within the folders
- // menulist has been changed, we need to update the label of its
- // representing element.
- var menupopup = this._folderMenuList.menupopup;
- for (let i = 0; i < menupopup.childNodes.length; i++) {
- if ("folderId" in menupopup.childNodes[i] &&
- menupopup.childNodes[i].folderId == aItemId) {
- menupopup.childNodes[i].label = aValue;
- break;
- }
- }
- }
-
- return;
- }
-
- switch (aProperty) {
- case "title":
- var namePicker = this._element("namePicker");
- if (namePicker.value != aValue) {
- namePicker.value = aValue;
- this._editorTransactionManagerClear(namePicker);
- }
- break;
- case "uri":
- var locationField = this._element("locationField");
- if (locationField.value != aValue) {
- this._uri = Cc["@mozilla.org/network/io-service;1"].
- getService(Ci.nsIIOService).
- newURI(aValue, null, null);
- this._initTextField("locationField", this._uri.spec);
- this._initNamePicker();
- this._initTextField("tagsField",
- PlacesUtils.tagging
- .getTagsForURI(this._uri).join(", "),
- false);
- this._rebuildTagsSelectorList();
- }
- break;
- case "keyword":
- this._keyword = PlacesUtils.bookmarks.getKeywordForBookmark(this._itemId);
- this._initTextField("keywordField", this._keyword);
- break;
- case PlacesUIUtils.DESCRIPTION_ANNO:
- this._initTextField("descriptionField",
- PlacesUIUtils.getItemDescription(this._itemId));
- break;
- case PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO:
- this._element("loadInSidebarCheckbox").checked =
- PlacesUtils.annotations.itemHasAnnotation(this._itemId,
- PlacesUIUtils.LOAD_IN_SIDEBAR_ANNO);
- break;
- case PlacesUtils.LMANNO_FEEDURI:
- let feedURISpec =
- PlacesUtils.annotations.getItemAnnotation(this._itemId,
- PlacesUtils.LMANNO_FEEDURI);
- this._initTextField("feedLocationField", feedURISpec, true);
- break;
- case PlacesUtils.LMANNO_SITEURI:
- let siteURISpec = "";
- try {
- siteURISpec =
- PlacesUtils.annotations.getItemAnnotation(this._itemId,
- PlacesUtils.LMANNO_SITEURI);
- } catch (ex) {}
- this._initTextField("siteLocationField", siteURISpec, true);
- break;
- }
- },
-
- onItemMoved: function EIO_onItemMoved(aItemId, aOldParent, aOldIndex,
- aNewParent, aNewIndex, aItemType) {
- if (aItemId != this._itemId ||
- aNewParent == this._getFolderIdFromMenuList())
- return;
-
- var folderItem = this._getFolderMenuItem(aNewParent);
-
- // just setting selectItem _does not_ trigger oncommand, so we don't
- // recurse
- this._folderMenuList.selectedItem = folderItem;
- },
-
- onItemAdded: function EIO_onItemAdded(aItemId, aParentId, aIndex, aItemType,
- aURI) {
- this._lastNewItem = aItemId;
- },
-
- onItemRemoved: function() { },
- onBeginUpdateBatch: function() { },
- onEndUpdateBatch: function() { },
- onItemVisited: function() { },
-};
diff --git a/components/places/content/editBookmarkOverlay.xul b/components/places/content/editBookmarkOverlay.xul
deleted file mode 100644
index 196369d..0000000
--- a/components/places/content/editBookmarkOverlay.xul
+++ /dev/null
@@ -1,228 +0,0 @@
-<!-- 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/. -->
-
-<!DOCTYPE overlay [
-<!ENTITY % editBookmarkOverlayDTD SYSTEM "chrome://browser/locale/places/editBookmarkOverlay.dtd">
-%editBookmarkOverlayDTD;
-]>
-
-<?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
-<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
-
-<overlay id="editBookmarkOverlay"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <vbox id="editBookmarkPanelContent" flex="1">
- <broadcaster id="paneElementsBroadcaster"/>
-
- <hbox id="editBMPanel_selectionCount" hidden="true" pack="center">
- <label id="editBMPanel_itemsCountText"/>
- </hbox>
-
- <grid id="editBookmarkPanelGrid" flex="1">
- <columns id="editBMPanel_columns">
- <column id="editBMPanel_labelColumn" />
- <column flex="1" id="editBMPanel_editColumn" />
- </columns>
- <rows id="editBMPanel_rows">
- <row id="editBMPanel_nameRow"
- align="center"
- collapsed="true">
- <label value="&editBookmarkOverlay.name.label;"
- class="editBMPanel_rowLabel"
- accesskey="&editBookmarkOverlay.name.accesskey;"
- control="editBMPanel_namePicker"
- observes="paneElementsBroadcaster"/>
- <textbox id="editBMPanel_namePicker"
- observes="paneElementsBroadcaster"/>
- </row>
-
- <row id="editBMPanel_locationRow"
- align="center"
- collapsed="true">
- <label value="&editBookmarkOverlay.location.label;"
- class="editBMPanel_rowLabel"
- accesskey="&editBookmarkOverlay.location.accesskey;"
- control="editBMPanel_locationField"
- observes="paneElementsBroadcaster"/>
- <textbox id="editBMPanel_locationField"
- class="uri-element"
- observes="paneElementsBroadcaster"/>
- </row>
-
- <row id="editBMPanel_feedLocationRow"
- align="center"
- collapsed="true">
- <label value="&editBookmarkOverlay.feedLocation.label;"
- class="editBMPanel_rowLabel"
- accesskey="&editBookmarkOverlay.feedLocation.accesskey;"
- control="editBMPanel_feedLocationField"
- observes="paneElementsBroadcaster"/>
- <textbox id="editBMPanel_feedLocationField"
- class="uri-element"
- observes="paneElementsBroadcaster"/>
- </row>
-
- <row id="editBMPanel_siteLocationRow"
- align="center"
- collapsed="true">
- <label value="&editBookmarkOverlay.siteLocation.label;"
- class="editBMPanel_rowLabel"
- accesskey="&editBookmarkOverlay.siteLocation.accesskey;"
- control="editBMPanel_siteLocationField"
- observes="paneElementsBroadcaster"/>
- <textbox id="editBMPanel_siteLocationField"
- class="uri-element"
- observes="paneElementsBroadcaster"/>
- </row>
-
- <row id="editBMPanel_folderRow"
- align="center"
- collapsed="true">
- <label value="&editBookmarkOverlay.folder.label;"
- class="editBMPanel_rowLabel"
- control="editBMPanel_folderMenuList"
- observes="paneElementsBroadcaster"/>
- <hbox flex="1" align="center">
- <menulist id="editBMPanel_folderMenuList"
- class="folder-icon"
- flex="1"
- oncommand="gEditItemOverlay.onFolderMenuListCommand(event);"
- observes="paneElementsBroadcaster">
- <menupopup>
- <!-- Static item for special folders -->
- <menuitem id="editBMPanel_toolbarFolderItem"
- class="menuitem-iconic folder-icon"/>
- <menuitem id="editBMPanel_bmRootItem"
- class="menuitem-iconic folder-icon"/>
- <menuitem id="editBMPanel_unfiledRootItem"
- class="menuitem-iconic folder-icon"/>
- <menuseparator id="editBMPanel_chooseFolderSeparator"/>
- <menuitem id="editBMPanel_chooseFolderMenuItem"
- label="&editBookmarkOverlay.choose.label;"
- class="menuitem-iconic folder-icon"/>
- <menuseparator id="editBMPanel_foldersSeparator" hidden="true"/>
- </menupopup>
- </menulist>
- <button id="editBMPanel_foldersExpander"
- class="expander-down"
- tooltiptext="&editBookmarkOverlay.foldersExpanderDown.tooltip;"
- tooltiptextdown="&editBookmarkOverlay.foldersExpanderDown.tooltip;"
- tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
- oncommand="gEditItemOverlay.toggleFolderTreeVisibility();"
- observes="paneElementsBroadcaster"/>
- </hbox>
- </row>
-
- <row id="editBMPanel_folderTreeRow"
- collapsed="true"
- flex="1">
- <spacer/>
- <vbox flex="1">
- <tree id="editBMPanel_folderTree"
- flex="1"
- class="placesTree"
- type="places"
- height="150"
- minheight="150"
- editable="true"
- onselect="gEditItemOverlay.onFolderTreeSelect();"
- hidecolumnpicker="true"
- observes="paneElementsBroadcaster">
- <treecols>
- <treecol anonid="title" flex="1" primary="true" hideheader="true"/>
- </treecols>
- <treechildren flex="1"/>
- </tree>
-
- <hbox id="editBMPanel_newFolderBox">
- <button label="&editBookmarkOverlay.newFolderButton.label;"
- id="editBMPanel_newFolderButton"
- accesskey="&editBookmarkOverlay.newFolderButton.accesskey;"
- oncommand="gEditItemOverlay.newFolder();"/>
- </hbox>
- </vbox>
- </row>
-
- <row id="editBMPanel_tagsRow"
- align="center"
- collapsed="true">
- <label value="&editBookmarkOverlay.tags.label;"
- class="editBMPanel_rowLabel"
- accesskey="&editBookmarkOverlay.tags.accesskey;"
- control="editBMPanel_tagsField"
- observes="paneElementsBroadcaster"/>
- <hbox flex="1" align="center">
- <textbox id="editBMPanel_tagsField"
- type="autocomplete"
- class="padded"
- flex="1"
- autocompletesearch="places-tag-autocomplete"
- completedefaultindex="true"
- tabscrolling="true"
- showcommentcolumn="true"
- observes="paneElementsBroadcaster"
- placeholder="&editBookmarkOverlay.tagsEmptyDesc.label;"/>
- <button id="editBMPanel_tagsSelectorExpander"
- class="expander-down"
- tooltiptext="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
- tooltiptextdown="&editBookmarkOverlay.tagsExpanderDown.tooltip;"
- tooltiptextup="&editBookmarkOverlay.expanderUp.tooltip;"
- oncommand="gEditItemOverlay.toggleTagsSelector();"
- observes="paneElementsBroadcaster"/>
- </hbox>
- </row>
-
- <row id="editBMPanel_tagsSelectorRow"
- align="center"
- collapsed="true">
- <spacer/>
- <listbox id="editBMPanel_tagsSelector"
- height="150"
- observes="paneElementsBroadcaster"/>
- </row>
-
- <row id="editBMPanel_keywordRow"
- align="center"
- collapsed="true">
- <observes element="additionalInfoBroadcaster" attribute="hidden"/>
- <label value="&editBookmarkOverlay.keyword.label;"
- class="editBMPanel_rowLabel"
- accesskey="&editBookmarkOverlay.keyword.accesskey;"
- control="editBMPanel_keywordField"
- observes="paneElementsBroadcaster"/>
- <textbox id="editBMPanel_keywordField"
- observes="paneElementsBroadcaster"/>
- </row>
-
- <row id="editBMPanel_descriptionRow"
- collapsed="true">
- <observes element="additionalInfoBroadcaster" attribute="hidden"/>
- <label value="&editBookmarkOverlay.description.label;"
- class="editBMPanel_rowLabel"
- accesskey="&editBookmarkOverlay.description.accesskey;"
- control="editBMPanel_descriptionField"
- observes="paneElementsBroadcaster"/>
- <textbox id="editBMPanel_descriptionField"
- multiline="true"
- observes="paneElementsBroadcaster"/>
- </row>
- </rows>
- </grid>
-
- <checkbox id="editBMPanel_loadInSidebarCheckbox"
- collapsed="true"
- label="&editBookmarkOverlay.loadInSidebar.label;"
- accesskey="&editBookmarkOverlay.loadInSidebar.accesskey;"
- oncommand="gEditItemOverlay.onLoadInSidebarCheckboxCommand();"
- observes="paneElementsBroadcaster">
- <observes element="additionalInfoBroadcaster" attribute="hidden"/>
- </checkbox>
-
- <!-- If the ids are changing or additional fields are being added, be sure
- to sync the values in places.js -->
- <broadcaster id="additionalInfoBroadcaster"/>
-
- </vbox>
-</overlay>
diff --git a/components/places/content/history-panel.js b/components/places/content/history-panel.js
deleted file mode 100644
index cda39dd..0000000
--- a/components/places/content/history-panel.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* 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/. */
-
-var gHistoryTree;
-var gSearchBox;
-var gHistoryGrouping = "";
-var gSearching = false;
-
-function HistorySidebarInit()
-{
- gHistoryTree = document.getElementById("historyTree");
- gSearchBox = document.getElementById("search-box");
-
- gHistoryGrouping = document.getElementById("viewButton").
- getAttribute("selectedsort");
-
- if (gHistoryGrouping == "site")
- document.getElementById("bysite").setAttribute("checked", "true");
- else if (gHistoryGrouping == "visited")
- document.getElementById("byvisited").setAttribute("checked", "true");
- else if (gHistoryGrouping == "lastvisited")
- document.getElementById("bylastvisited").setAttribute("checked", "true");
- else if (gHistoryGrouping == "dayandsite")
- document.getElementById("bydayandsite").setAttribute("checked", "true");
- else
- document.getElementById("byday").setAttribute("checked", "true");
-
- searchHistory("");
-}
-
-function GroupBy(groupingType)
-{
- gHistoryGrouping = groupingType;
- searchHistory(gSearchBox.value);
-}
-
-function searchHistory(aInput)
-{
- var query = PlacesUtils.history.getNewQuery();
- var options = PlacesUtils.history.getNewQueryOptions();
-
- const NHQO = Ci.nsINavHistoryQueryOptions;
- var sortingMode;
- var resultType;
-
- switch (gHistoryGrouping) {
- case "visited":
- resultType = NHQO.RESULTS_AS_URI;
- sortingMode = NHQO.SORT_BY_VISITCOUNT_DESCENDING;
- break;
- case "lastvisited":
- resultType = NHQO.RESULTS_AS_URI;
- sortingMode = NHQO.SORT_BY_DATE_DESCENDING;
- break;
- case "dayandsite":
- resultType = NHQO.RESULTS_AS_DATE_SITE_QUERY;
- break;
- case "site":
- resultType = NHQO.RESULTS_AS_SITE_QUERY;
- sortingMode = NHQO.SORT_BY_TITLE_ASCENDING;
- break;
- case "day":
- default:
- resultType = NHQO.RESULTS_AS_DATE_QUERY;
- break;
- }
-
- if (aInput) {
- query.searchTerms = aInput;
- if (gHistoryGrouping != "visited" && gHistoryGrouping != "lastvisited") {
- sortingMode = NHQO.SORT_BY_FRECENCY_DESCENDING;
- resultType = NHQO.RESULTS_AS_URI;
- }
- }
-
- options.sortingMode = sortingMode;
- options.resultType = resultType;
- options.includeHidden = !!aInput;
-
- // call load() on the tree manually
- // instead of setting the place attribute in history-panel.xul
- // otherwise, we will end up calling load() twice
- gHistoryTree.load([query], options);
-}
-
-window.addEventListener("SidebarFocused",
- function()
- gSearchBox.focus(),
- false);
diff --git a/components/places/content/history-panel.xul b/components/places/content/history-panel.xul
deleted file mode 100644
index d1c875a..0000000
--- a/components/places/content/history-panel.xul
+++ /dev/null
@@ -1,95 +0,0 @@
-<?xml version="1.0"?> <!-- -*- Mode: xml; indent-tabs-mode: nil; -*- -->
-
-# 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/.
-
-<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
-<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
-
-<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
-<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
-
-<!DOCTYPE page [
-<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
-%placesDTD;
-]>
-
-<!-- we need to keep id="history-panel" for upgrade and switching
- between versions of the browser -->
-
-<page id="history-panel" orient="vertical"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- onload="HistorySidebarInit();"
- onunload="SidebarUtils.setMouseoverURL('');">
-
- <script type="application/javascript"
- src="chrome://browser/content/bookmarks/sidebarUtils.js"/>
- <script type="application/javascript"
- src="chrome://browser/content/places/history-panel.js"/>
-
- <commandset id="editMenuCommands"/>
- <commandset id="placesCommands"/>
-
- <keyset id="editMenuKeys">
-#ifdef XP_MACOSX
- <key id="key_delete2" keycode="VK_BACK" command="cmd_delete"/>
-#endif
- </keyset>
-
- <!-- required to overlay the context menu -->
- <menupopup id="placesContext"/>
-
- <!-- Bookmarks and history tooltip -->
- <tooltip id="bhTooltip"/>
-
- <hbox id="sidebar-search-container" align="center">
- <label id="sidebar-search-label"
- value="&find.label;" accesskey="&find.accesskey;"
- control="search-box"/>
- <textbox id="search-box" flex="1" type="search" class="compact"
- aria-controls="historyTree"
- oncommand="searchHistory(this.value);"/>
- <button id="viewButton" style="min-width:0px !important;" type="menu"
- label="&view.label;" accesskey="&view.accesskey;" selectedsort="day"
- persist="selectedsort">
- <menupopup>
- <menuitem id="bydayandsite" label="&byDayAndSite.label;"
- accesskey="&byDayAndSite.accesskey;" type="radio"
- oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'dayandsite'); GroupBy('dayandsite');"/>
- <menuitem id="bysite" label="&bySite.label;"
- accesskey="&bySite.accesskey;" type="radio"
- oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'site'); GroupBy('site');"/>
- <menuitem id="byday" label="&byDate.label;"
- accesskey="&byDate.accesskey;"
- type="radio"
- oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'day'); GroupBy('day');"/>
- <menuitem id="byvisited" label="&byMostVisited.label;"
- accesskey="&byMostVisited.accesskey;"
- type="radio"
- oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'visited'); GroupBy('visited');"/>
- <menuitem id="bylastvisited" label="&byLastVisited.label;"
- accesskey="&byLastVisited.accesskey;"
- type="radio"
- oncommand="this.parentNode.parentNode.setAttribute('selectedsort', 'lastvisited'); GroupBy('lastvisited');"/>
- </menupopup>
- </button>
- </hbox>
-
- <tree id="historyTree"
- class="sidebar-placesTree"
- flex="1"
- type="places"
- context="placesContext"
- hidecolumnpicker="true"
- onkeypress="SidebarUtils.handleTreeKeyPress(event);"
- onclick="SidebarUtils.handleTreeClick(this, event, true);"
- onmousemove="SidebarUtils.handleTreeMouseMove(event);"
- onmouseout="SidebarUtils.setMouseoverURL('');">
- <treecols>
- <treecol id="title" flex="1" primary="true" hideheader="true"/>
- </treecols>
- <treechildren class="sidebar-placesTreechildren" flex="1" tooltip="bhTooltip"/>
- </tree>
-</page>
diff --git a/components/places/content/menu.xml b/components/places/content/menu.xml
deleted file mode 100644
index d4041ec..0000000
--- a/components/places/content/menu.xml
+++ /dev/null
@@ -1,488 +0,0 @@
-<?xml version="1.0"?>
-
-# 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/.
-
-<bindings id="placesMenuBindings"
- xmlns="http://www.mozilla.org/xbl"
- xmlns:xbl="http://www.mozilla.org/xbl"
- xmlns:html="http://www.w3.org/1999/xhtml"
- xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <binding id="places-popup-base"
- extends="chrome://global/content/bindings/popup.xml#popup">
- <content>
- <xul:hbox flex="1">
- <xul:vbox class="menupopup-drop-indicator-bar" hidden="true">
- <xul:image class="menupopup-drop-indicator" mousethrough="always"/>
- </xul:vbox>
- <xul:arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
- smoothscroll="false">
- <children/>
- </xul:arrowscrollbox>
- </xul:hbox>
- </content>
-
- <implementation>
-
- <field name="_indicatorBar">
- document.getAnonymousElementByAttribute(this, "class",
- "menupopup-drop-indicator-bar");
- </field>
-
- <field name="_scrollBox">
- document.getAnonymousElementByAttribute(this, "class",
- "popup-internal-box");
- </field>
-
- <!-- This is the view that manage the popup -->
- <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
-
- <!-- Check if we should hide the drop indicator for the target -->
- <method name="_hideDropIndicator">
- <parameter name="aEvent"/>
- <body><![CDATA[
- let target = aEvent.target;
-
- // Don't draw the drop indicator outside of markers.
- // The markers are hidden, since otherwise sometimes popups acquire
- // scrollboxes on OS X, so we can't use them directly.
- let firstChildTop = this._startMarker.nextSibling.boxObject.y;
- let lastChildBottom = this._endMarker.previousSibling.boxObject.y +
- this._endMarker.previousSibling.boxObject.height;
- let betweenMarkers = target.boxObject.y >= firstChildTop ||
- target.boxObject.y <= lastChildBottom;
-
- // Hide the dropmarker if current node is not a Places node.
- return !(target && target._placesNode && betweenMarkers);
- ]]></body>
- </method>
-
- <!-- This function returns information about where to drop when
- dragging over this popup insertion point -->
- <method name="_getDropPoint">
- <parameter name="aEvent"/>
- <body><![CDATA[
- // Can't drop if the menu isn't a folder
- let resultNode = this._placesNode;
-
- if (!PlacesUtils.nodeIsFolder(resultNode) ||
- PlacesControllerDragHelper.disallowInsertion(resultNode)) {
- return null;
- }
-
- var dropPoint = { ip: null, folderElt: null };
-
- // The element we are dragging over
- let elt = aEvent.target;
- if (elt.localName == "menupopup")
- elt = elt.parentNode;
-
- // Calculate positions taking care of arrowscrollbox
- let eventY = aEvent.layerY;
- let scrollbox = this._scrollBox;
- let scrollboxOffset = scrollbox.scrollBoxObject.y -
- (scrollbox.boxObject.y - this.boxObject.y);
- let eltY = elt.boxObject.y - scrollboxOffset;
- let eltHeight = elt.boxObject.height;
-
- if (!elt._placesNode) {
- // If we are dragging over a non places node drop at the end.
- dropPoint.ip = new InsertionPoint(
- PlacesUtils.getConcreteItemId(resultNode),
- -1,
- Ci.nsITreeView.DROP_ON);
- // We can set folderElt if we are dropping over a static menu that
- // has an internal placespopup.
- let isMenu = elt.localName == "menu" ||
- (elt.localName == "toolbarbutton" &&
- elt.getAttribute("type") == "menu");
- if (isMenu && elt.lastChild &&
- elt.lastChild.hasAttribute("placespopup"))
- dropPoint.folderElt = elt;
- return dropPoint;
- }
- if ((PlacesUtils.nodeIsFolder(elt._placesNode) &&
- !PlacesUIUtils.isContentsReadOnly(elt._placesNode)) ||
- PlacesUtils.nodeIsTagQuery(elt._placesNode)) {
- // This is a folder or a tag container.
- if (eventY - eltY < eltHeight * 0.20) {
- // If mouse is in the top part of the element, drop above folder.
- dropPoint.ip = new InsertionPoint(
- PlacesUtils.getConcreteItemId(resultNode),
- -1,
- Ci.nsITreeView.DROP_BEFORE,
- PlacesUtils.nodeIsTagQuery(elt._placesNode),
- elt._placesNode.itemId);
- return dropPoint;
- }
- else if (eventY - eltY < eltHeight * 0.80) {
- // If mouse is in the middle of the element, drop inside folder.
- dropPoint.ip = new InsertionPoint(
- PlacesUtils.getConcreteItemId(elt._placesNode),
- -1,
- Ci.nsITreeView.DROP_ON,
- PlacesUtils.nodeIsTagQuery(elt._placesNode));
- dropPoint.folderElt = elt;
- return dropPoint;
- }
- }
- else if (eventY - eltY <= eltHeight / 2) {
- // This is a non-folder node or a readonly folder.
- // If the mouse is above the middle, drop above this item.
- dropPoint.ip = new InsertionPoint(
- PlacesUtils.getConcreteItemId(resultNode),
- -1,
- Ci.nsITreeView.DROP_BEFORE,
- PlacesUtils.nodeIsTagQuery(elt._placesNode),
- elt._placesNode.itemId);
- return dropPoint;
- }
-
- // Drop below the item.
- dropPoint.ip = new InsertionPoint(
- PlacesUtils.getConcreteItemId(resultNode),
- -1,
- Ci.nsITreeView.DROP_AFTER,
- PlacesUtils.nodeIsTagQuery(elt._placesNode),
- elt._placesNode.itemId);
- return dropPoint;
- ]]></body>
- </method>
-
- <!-- Sub-menus should be opened when the mouse drags over them, and closed
- when the mouse drags off. The overFolder object manages opening and
- closing of folders when the mouse hovers. -->
- <field name="_overFolder"><![CDATA[({
- _self: this,
- _folder: {elt: null,
- openTimer: null,
- hoverTime: 350,
- closeTimer: null},
- _closeMenuTimer: null,
-
- get elt() {
- return this._folder.elt;
- },
- set elt(val) {
- return this._folder.elt = val;
- },
-
- get openTimer() {
- return this._folder.openTimer;
- },
- set openTimer(val) {
- return this._folder.openTimer = val;
- },
-
- get hoverTime() {
- return this._folder.hoverTime;
- },
- set hoverTime(val) {
- return this._folder.hoverTime = val;
- },
-
- get closeTimer() {
- return this._folder.closeTimer;
- },
- set closeTimer(val) {
- return this._folder.closeTimer = val;
- },
-
- get closeMenuTimer() {
- return this._closeMenuTimer;
- },
- set closeMenuTimer(val) {
- return this._closeMenuTimer = val;
- },
-
- setTimer: function OF__setTimer(aTime) {
- var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- timer.initWithCallback(this, aTime, timer.TYPE_ONE_SHOT);
- return timer;
- },
-
- notify: function OF__notify(aTimer) {
- // Function to process all timer notifications.
-
- if (aTimer == this._folder.openTimer) {
- // Timer to open a submenu that's being dragged over.
- this._folder.elt.lastChild.setAttribute("autoopened", "true");
- this._folder.elt.lastChild.showPopup(this._folder.elt);
- this._folder.openTimer = null;
- }
-
- else if (aTimer == this._folder.closeTimer) {
- // Timer to close a submenu that's been dragged off of.
- // Only close the submenu if the mouse isn't being dragged over any
- // of its child menus.
- var draggingOverChild = PlacesControllerDragHelper
- .draggingOverChildNode(this._folder.elt);
- if (draggingOverChild)
- this._folder.elt = null;
- this.clear();
-
- // Close any parent folders which aren't being dragged over.
- // (This is necessary because of the above code that keeps a folder
- // open while its children are being dragged over.)
- if (!draggingOverChild)
- this.closeParentMenus();
- }
-
- else if (aTimer == this.closeMenuTimer) {
- // Timer to close this menu after the drag exit.
- var popup = this._self;
- // if we are no more dragging we can leave the menu open to allow
- // for better D&D bookmark organization
- if (PlacesControllerDragHelper.getSession() &&
- !PlacesControllerDragHelper.draggingOverChildNode(popup.parentNode)) {
- popup.hidePopup();
- // Close any parent menus that aren't being dragged over;
- // otherwise they'll stay open because they couldn't close
- // while this menu was being dragged over.
- this.closeParentMenus();
- }
- this._closeMenuTimer = null;
- }
- },
-
- // Helper function to close all parent menus of this menu,
- // as long as none of the parent's children are currently being
- // dragged over.
- closeParentMenus: function OF__closeParentMenus() {
- var popup = this._self;
- var parent = popup.parentNode;
- while (parent) {
- if (parent.localName == "menupopup" && parent._placesNode) {
- if (PlacesControllerDragHelper.draggingOverChildNode(parent.parentNode))
- break;
- parent.hidePopup();
- }
- parent = parent.parentNode;
- }
- },
-
- // The mouse is no longer dragging over the stored menubutton.
- // Close the menubutton, clear out drag styles, and clear all
- // timers for opening/closing it.
- clear: function OF__clear() {
- if (this._folder.elt && this._folder.elt.lastChild) {
- if (!this._folder.elt.lastChild.hasAttribute("dragover"))
- this._folder.elt.lastChild.hidePopup();
- // remove menuactive style
- this._folder.elt.removeAttribute("_moz-menuactive");
- this._folder.elt = null;
- }
- if (this._folder.openTimer) {
- this._folder.openTimer.cancel();
- this._folder.openTimer = null;
- }
- if (this._folder.closeTimer) {
- this._folder.closeTimer.cancel();
- this._folder.closeTimer = null;
- }
- }
- })]]></field>
-
- <method name="_cleanupDragDetails">
- <body><![CDATA[
- // Called on dragend and drop.
- PlacesControllerDragHelper.currentDropTarget = null;
- this._rootView._draggedElt = null;
- this.removeAttribute("dragover");
- this.removeAttribute("dragstart");
- this._indicatorBar.hidden = true;
- ]]></body>
- </method>
-
- </implementation>
-
- <handlers>
- <handler event="DOMMenuItemActive"><![CDATA[
- let elt = event.target;
- if (elt.parentNode != this)
- return;
-
-#ifdef XP_MACOSX
- // XXX: The following check is a temporary hack until bug 420033 is
- // resolved.
- let parentElt = elt.parent;
- while (parentElt) {
- if (parentElt.id == "bookmarksMenuPopup" ||
- parentElt.id == "goPopup")
- return;
-
- parentElt = parentElt.parentNode;
- }
-#endif
-
- if (window.XULBrowserWindow) {
- let elt = event.target;
- let placesNode = elt._placesNode;
-
- var linkURI;
- if (placesNode && PlacesUtils.nodeIsURI(placesNode))
- linkURI = placesNode.uri;
- else if (elt.hasAttribute("targetURI"))
- linkURI = elt.getAttribute("targetURI");
-
- if (linkURI)
- window.XULBrowserWindow.setOverLink(linkURI, null);
- }
- ]]></handler>
-
- <handler event="DOMMenuItemInactive"><![CDATA[
- let elt = event.target;
- if (elt.parentNode != this)
- return;
-
- if (window.XULBrowserWindow)
- window.XULBrowserWindow.setOverLink("", null);
- ]]></handler>
-
- <handler event="dragstart"><![CDATA[
- if (!event.target._placesNode)
- return;
-
- let draggedElt = event.target._placesNode;
-
- // Force a copy action if parent node is a query or we are dragging a
- // not-removable node.
- if (!PlacesControllerDragHelper.canMoveNode(draggedElt))
- event.dataTransfer.effectAllowed = "copyLink";
-
- // Activate the view and cache the dragged element.
- this._rootView._draggedElt = draggedElt;
- this._rootView.controller.setDataTransfer(event);
- this.setAttribute("dragstart", "true");
- event.stopPropagation();
- ]]></handler>
-
- <handler event="drop"><![CDATA[
- PlacesControllerDragHelper.currentDropTarget = event.target;
-
- let dropPoint = this._getDropPoint(event);
- if (dropPoint && dropPoint.ip) {
- PlacesControllerDragHelper.onDrop(dropPoint.ip, event.dataTransfer);
- event.preventDefault();
- }
-
- this._cleanupDragDetails();
- event.stopPropagation();
- ]]></handler>
-
- <handler event="dragover"><![CDATA[
- PlacesControllerDragHelper.currentDropTarget = event.target;
- let dt = event.dataTransfer;
-
- let dropPoint = this._getDropPoint(event);
- if (!dropPoint || !dropPoint.ip ||
- !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
- this._indicatorBar.hidden = true;
- event.stopPropagation();
- return;
- }
-
- // Mark this popup as being dragged over.
- this.setAttribute("dragover", "true");
-
- if (dropPoint.folderElt) {
- // We are dragging over a folder.
- // _overFolder should take the care of opening it on a timer.
- if (this._overFolder.elt &&
- this._overFolder.elt != dropPoint.folderElt) {
- // We are dragging over a new folder, let's clear old values
- this._overFolder.clear();
- }
- if (!this._overFolder.elt) {
- this._overFolder.elt = dropPoint.folderElt;
- // Create the timer to open this folder.
- this._overFolder.openTimer = this._overFolder
- .setTimer(this._overFolder.hoverTime);
- }
- // Since we are dropping into a folder set the corresponding style.
- dropPoint.folderElt.setAttribute("_moz-menuactive", true);
- }
- else {
- // We are not dragging over a folder.
- // Clear out old _overFolder information.
- this._overFolder.clear();
- }
-
- // Autoscroll the popup strip if we drag over the scroll buttons.
- let anonid = event.originalTarget.getAttribute('anonid');
- let scrollDir = anonid == "scrollbutton-up" ? -1 :
- anonid == "scrollbutton-down" ? 1 : 0;
- if (scrollDir != 0) {
- this._scrollBox.scrollByIndex(scrollDir, false);
- }
-
- // Check if we should hide the drop indicator for this target.
- if (dropPoint.folderElt || this._hideDropIndicator(event)) {
- this._indicatorBar.hidden = true;
- event.preventDefault();
- event.stopPropagation();
- return;
- }
-
- // We should display the drop indicator relative to the arrowscrollbox.
- let sbo = this._scrollBox.scrollBoxObject;
- let newMarginTop = 0;
- if (scrollDir == 0) {
- let elt = this.firstChild;
- while (elt && event.screenY > elt.boxObject.screenY +
- elt.boxObject.height / 2)
- elt = elt.nextSibling;
- newMarginTop = elt ? elt.boxObject.screenY - sbo.screenY :
- sbo.height;
- }
- else if (scrollDir == 1)
- newMarginTop = sbo.height;
-
- // Set the new marginTop based on arrowscrollbox.
- newMarginTop += sbo.y - this._scrollBox.boxObject.y;
- this._indicatorBar.firstChild.style.marginTop = newMarginTop + "px";
- this._indicatorBar.hidden = false;
-
- event.preventDefault();
- event.stopPropagation();
- ]]></handler>
-
- <handler event="dragexit"><![CDATA[
- PlacesControllerDragHelper.currentDropTarget = null;
- this.removeAttribute("dragover");
-
- // If we have not moved to a valid new target clear the drop indicator
- // this happens when moving out of the popup.
- let target = event.relatedTarget;
- if (!target)
- this._indicatorBar.hidden = true;
-
- // Close any folder being hovered over
- if (this._overFolder.elt) {
- this._overFolder.closeTimer = this._overFolder
- .setTimer(this._overFolder.hoverTime);
- }
-
- // The autoopened attribute is set when this folder was automatically
- // opened after the user dragged over it. If this attribute is set,
- // auto-close the folder on drag exit.
- // We should also try to close this popup if the drag has started
- // from here, the timer will check if we are dragging over a child.
- if (this.hasAttribute("autoopened") ||
- this.hasAttribute("dragstart")) {
- this._overFolder.closeMenuTimer = this._overFolder
- .setTimer(this._overFolder.hoverTime);
- }
-
- event.stopPropagation();
- ]]></handler>
-
- <handler event="dragend"><![CDATA[
- this._cleanupDragDetails();
- ]]></handler>
-
- </handlers>
- </binding>
-</bindings>
diff --git a/components/places/content/moveBookmarks.js b/components/places/content/moveBookmarks.js
deleted file mode 100644
index 964604f..0000000
--- a/components/places/content/moveBookmarks.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-var gMoveBookmarksDialog = {
- _nodes: null,
-
- _foldersTree: null,
- get foldersTree() {
- if (!this._foldersTree)
- this._foldersTree = document.getElementById("foldersTree");
-
- return this._foldersTree;
- },
-
- init: function() {
- this._nodes = window.arguments[0];
-
- this.foldersTree.place =
- "place:excludeItems=1&excludeQueries=1&excludeReadOnlyFolders=1&folder=" +
- PlacesUIUtils.allBookmarksFolderId;
- },
-
- onOK: function MBD_onOK(aEvent) {
- var selectedNode = this.foldersTree.selectedNode;
- NS_ASSERT(selectedNode,
- "selectedNode must be set in a single-selection tree with initial selection set");
- var selectedFolderID = PlacesUtils.getConcreteItemId(selectedNode);
-
- var transactions = [];
- for (var i=0; i < this._nodes.length; i++) {
- // Nothing to do if the node is already under the selected folder
- if (this._nodes[i].parent.itemId == selectedFolderID)
- continue;
-
- let txn = new PlacesMoveItemTransaction(this._nodes[i].itemId,
- selectedFolderID,
- PlacesUtils.bookmarks.DEFAULT_INDEX);
- transactions.push(txn);
- }
-
- if (transactions.length != 0) {
- let txn = new PlacesAggregatedTransaction("Move Items", transactions);
- PlacesUtils.transactionManager.doTransaction(txn);
- }
- },
-
- newFolder: function MBD_newFolder() {
- // The command is disabled when the tree is not focused
- this.foldersTree.focus();
- goDoCommand("placesCmd_new:folder");
- }
-};
diff --git a/components/places/content/moveBookmarks.xul b/components/places/content/moveBookmarks.xul
deleted file mode 100644
index b6e75f3..0000000
--- a/components/places/content/moveBookmarks.xul
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0"?>
-
-<!-- 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/. -->
-
-<?xml-stylesheet href="chrome://global/skin/"?>
-<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
-<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
-
-<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
-
-<!DOCTYPE window [
- <!ENTITY % moveBookmarksDTD SYSTEM "chrome://browser/locale/places/moveBookmarks.dtd">
- %moveBookmarksDTD;
-]>
-
-<dialog id="moveBookmarkDialog"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- ondialogaccept="return gMoveBookmarksDialog.onOK(event);"
- title="&window.title;"
- onload="gMoveBookmarksDialog.init();"
- style="&window.style;"
- screenX="24"
- screenY="24"
- persist="screenX screenY width height">
-
- <script type="application/javascript"
- src="chrome://browser/content/places/moveBookmarks.js"/>
-
- <hbox flex="1">
- <label id="movetolabel" value="&moveTo.label;" control="foldersTree"/>
- <hbox flex="1">
- <tree id="foldersTree"
- class="placesTree"
- flex="1"
- type="places"
- seltype="single"
- hidecolumnpicker="true">
- <treecols>
- <treecol id="title" flex="1" primary="true" hideheader="true"/>
- </treecols>
- <treechildren id="placesListChildren" view="placesList" flex="1"/>
- </tree>
- <vbox>
- <button id="newFolderButton"
- label="&newFolderButton.label;"
- accesskey="&newFolderButton.accesskey;"
- oncommand="gMoveBookmarksDialog.newFolder();"/>
- </vbox>
- </hbox>
- </hbox>
-</dialog>
diff --git a/components/places/content/organizer.css b/components/places/content/organizer.css
deleted file mode 100644
index 47b1832..0000000
--- a/components/places/content/organizer.css
+++ /dev/null
@@ -1,7 +0,0 @@
-/* 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/. */
-
-#searchFilter {
- width: 23em;
-}
diff --git a/components/places/content/places.css b/components/places/content/places.css
deleted file mode 100644
index 5151cca..0000000
--- a/components/places/content/places.css
+++ /dev/null
@@ -1,16 +0,0 @@
-/* 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/. */
-
-tree[type="places"] {
- -moz-binding: url("chrome://browser/content/places/tree.xml#places-tree");
-}
-
-.toolbar-drop-indicator {
- position: relative;
- z-index: 1;
-}
-
-menupopup[placespopup="true"] {
- -moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-base");
-}
diff --git a/components/places/content/places.js b/components/places/content/places.js
deleted file mode 100644
index 40dbcb9..0000000
--- a/components/places/content/places.js
+++ /dev/null
@@ -1,1553 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
-
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Task",
- "resource://gre/modules/Task.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "BookmarkJSONUtils",
- "resource://gre/modules/BookmarkJSONUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesBackups",
- "resource://gre/modules/PlacesBackups.jsm");
-
-const RESTORE_FILEPICKER_FILTER_EXT = "*.json;*.jsonlz4";
-
-var PlacesOrganizer = {
- _places: null,
-
- // IDs of fields from editBookmarkOverlay that should be hidden when infoBox
- // is minimal. IDs should be kept in sync with the IDs of the elements
- // observing additionalInfoBroadcaster.
- _additionalInfoFields: [
- "editBMPanel_descriptionRow",
- "editBMPanel_loadInSidebarCheckbox",
- "editBMPanel_keywordRow",
- ],
-
- _initFolderTree: function() {
- var leftPaneRoot = PlacesUIUtils.leftPaneFolderId;
- this._places.place = "place:excludeItems=1&expandQueries=0&folder=" + leftPaneRoot;
- },
-
- selectLeftPaneQuery: function PO_selectLeftPaneQuery(aQueryName) {
- var itemId = PlacesUIUtils.leftPaneQueries[aQueryName];
- this._places.selectItems([itemId]);
- // Forcefully expand all-bookmarks
- if (aQueryName == "AllBookmarks" || aQueryName == "History")
- PlacesUtils.asContainer(this._places.selectedNode).containerOpen = true;
- },
-
- init: function PO_init() {
- ContentArea.init();
-
- this._places = document.getElementById("placesList");
- this._initFolderTree();
-
- var leftPaneSelection = "AllBookmarks"; // default to all-bookmarks
- if (window.arguments && window.arguments[0])
- leftPaneSelection = window.arguments[0];
-
- this.selectLeftPaneQuery(leftPaneSelection);
- if (leftPaneSelection == "History") {
- let historyNode = this._places.selectedNode;
- if (historyNode.childCount > 0)
- this._places.selectNode(historyNode.getChild(0));
- }
- // clear the back-stack
- this._backHistory.splice(0, this._backHistory.length);
- document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true);
-
- // Set up the search UI.
- PlacesSearchBox.init();
-
- window.addEventListener("AppCommand", this, true);
-#ifdef XP_MACOSX
- // 1. Map Edit->Find command to OrganizerCommand_find:all. Need to map
- // both the menuitem and the Find key.
- var findMenuItem = document.getElementById("menu_find");
- findMenuItem.setAttribute("command", "OrganizerCommand_find:all");
- var findKey = document.getElementById("key_find");
- findKey.setAttribute("command", "OrganizerCommand_find:all");
-
- // 2. Disable some keybindings from browser.xul
- var elements = ["cmd_handleBackspace", "cmd_handleShiftBackspace"];
- for (var i=0; i < elements.length; i++) {
- document.getElementById(elements[i]).setAttribute("disabled", "true");
- }
-
- // 3. Disable the keyboard shortcut for the History menu back/forward
- // in order to support those in the Library
- var historyMenuBack = document.getElementById("historyMenuBack");
- historyMenuBack.removeAttribute("key");
- var historyMenuForward = document.getElementById("historyMenuForward");
- historyMenuForward.removeAttribute("key");
-#endif
-
- // remove the "Properties" context-menu item, we've our own details pane
- document.getElementById("placesContext")
- .removeChild(document.getElementById("placesContext_show:info"));
-
- ContentArea.focus();
- },
-
- QueryInterface: function PO_QueryInterface(aIID) {
- if (aIID.equals(Components.interfaces.nsIDOMEventListener) ||
- aIID.equals(Components.interfaces.nsISupports))
- return this;
-
- throw new Components.Exception("", Components.results.NS_NOINTERFACE);
- },
-
- handleEvent: function PO_handleEvent(aEvent) {
- if (aEvent.type != "AppCommand")
- return;
-
- aEvent.stopPropagation();
- switch (aEvent.command) {
- case "Back":
- if (this._backHistory.length > 0)
- this.back();
- break;
- case "Forward":
- if (this._forwardHistory.length > 0)
- this.forward();
- break;
- case "Search":
- PlacesSearchBox.findAll();
- break;
- }
- },
-
- destroy: function PO_destroy() {
- },
-
- _location: null,
- get location() {
- return this._location;
- },
-
- set location(aLocation) {
- if (!aLocation || this._location == aLocation)
- return aLocation;
-
- if (this.location) {
- this._backHistory.unshift(this.location);
- this._forwardHistory.splice(0, this._forwardHistory.length);
- }
-
- this._location = aLocation;
- this._places.selectPlaceURI(aLocation);
-
- if (!this._places.hasSelection) {
- // If no node was found for the given place: uri, just load it directly
- ContentArea.currentPlace = aLocation;
- }
- this.updateDetailsPane();
-
- // update navigation commands
- if (this._backHistory.length == 0)
- document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true);
- else
- document.getElementById("OrganizerCommand:Back").removeAttribute("disabled");
- if (this._forwardHistory.length == 0)
- document.getElementById("OrganizerCommand:Forward").setAttribute("disabled", true);
- else
- document.getElementById("OrganizerCommand:Forward").removeAttribute("disabled");
-
- return aLocation;
- },
-
- _backHistory: [],
- _forwardHistory: [],
-
- back: function PO_back() {
- this._forwardHistory.unshift(this.location);
- var historyEntry = this._backHistory.shift();
- this._location = null;
- this.location = historyEntry;
- },
- forward: function PO_forward() {
- this._backHistory.unshift(this.location);
- var historyEntry = this._forwardHistory.shift();
- this._location = null;
- this.location = historyEntry;
- },
-
- /**
- * Called when a place folder is selected in the left pane.
- * @param resetSearchBox
- * true if the search box should also be reset, false otherwise.
- * The search box should be reset when a new folder in the left
- * pane is selected; the search scope and text need to be cleared in
- * preparation for the new folder. Note that if the user manually
- * resets the search box, either by clicking its reset button or by
- * deleting its text, this will be false.
- */
- _cachedLeftPaneSelectedURI: null,
- onPlaceSelected: function PO_onPlaceSelected(resetSearchBox) {
- // Don't change the right-hand pane contents when there's no selection.
- if (!this._places.hasSelection)
- return;
-
- var node = this._places.selectedNode;
- var queries = PlacesUtils.asQuery(node).getQueries();
-
- // Items are only excluded on the left pane.
- var options = node.queryOptions.clone();
- options.excludeItems = false;
- var placeURI = PlacesUtils.history.queriesToQueryString(queries,
- queries.length,
- options);
-
- // If either the place of the content tree in the right pane has changed or
- // the user cleared the search box, update the place, hide the search UI,
- // and update the back/forward buttons by setting location.
- if (ContentArea.currentPlace != placeURI || !resetSearchBox) {
- ContentArea.currentPlace = placeURI;
- PlacesSearchBox.hideSearchUI();
- this.location = node.uri;
- }
-
- // Update the selected folder title where it appears in the UI: the folder
- // scope button, and the search box emptytext.
- // They must be updated even if the selection hasn't changed --
- // specifically when node's title changes. In that case a selection event
- // is generated, this method is called, but the selection does not change.
- var folderButton = document.getElementById("scopeBarFolder");
- var folderTitle = node.title || folderButton.getAttribute("emptytitle");
- folderButton.setAttribute("label", folderTitle);
- if (PlacesSearchBox.filterCollection == "collection")
- PlacesSearchBox.updateCollectionTitle(folderTitle);
-
- // When we invalidate a container we use suppressSelectionEvent, when it is
- // unset a select event is fired, in many cases the selection did not really
- // change, so we should check for it, and return early in such a case. Note
- // that we cannot return any earlier than this point, because when
- // !resetSearchBox, we need to update location and hide the UI as above,
- // even though the selection has not changed.
- if (node.uri == this._cachedLeftPaneSelectedURI)
- return;
- this._cachedLeftPaneSelectedURI = node.uri;
-
- // At this point, resetSearchBox is true, because the left pane selection
- // has changed; otherwise we would have returned earlier.
-
- PlacesSearchBox.searchFilter.reset();
- this._setSearchScopeForNode(node);
- this.updateDetailsPane();
- },
-
- /**
- * Sets the search scope based on aNode's properties.
- * @param aNode
- * the node to set up scope from
- */
- _setSearchScopeForNode: function PO__setScopeForNode(aNode) {
- let itemId = aNode.itemId;
-
- // Set default buttons status.
- let bookmarksButton = document.getElementById("scopeBarAll");
- bookmarksButton.hidden = false;
- let downloadsButton = document.getElementById("scopeBarDownloads");
- downloadsButton.hidden = true;
-
- if (PlacesUtils.nodeIsHistoryContainer(aNode) ||
- itemId == PlacesUIUtils.leftPaneQueries["History"]) {
- PlacesQueryBuilder.setScope("history");
- }
- else if (itemId == PlacesUIUtils.leftPaneQueries["Downloads"]) {
- downloadsButton.hidden = false;
- bookmarksButton.hidden = true;
- PlacesQueryBuilder.setScope("downloads");
- }
- else {
- // Default to All Bookmarks for all other nodes, per bug 469437.
- PlacesQueryBuilder.setScope("bookmarks");
- }
-
- // Enable or disable the folder scope button.
- let folderButton = document.getElementById("scopeBarFolder");
- folderButton.hidden = !PlacesUtils.nodeIsFolder(aNode) ||
- itemId == PlacesUIUtils.allBookmarksFolderId;
- },
-
- /**
- * Handle clicks on the places list.
- * Single Left click, right click or modified click do not result in any
- * special action, since they're related to selection.
- * @param aEvent
- * The mouse event.
- */
- onPlacesListClick: function PO_onPlacesListClick(aEvent) {
- // Only handle clicks on tree children.
- if (aEvent.target.localName != "treechildren")
- return;
-
- let node = this._places.selectedNode;
- if (node) {
- let middleClick = aEvent.button == 1 && aEvent.detail == 1;
- if (middleClick && PlacesUtils.nodeIsContainer(node)) {
- // The command execution function will take care of seeing if the
- // selection is a folder or a different container type, and will
- // load its contents in tabs.
- PlacesUIUtils.openContainerNodeInTabs(selectedNode, aEvent, this._places);
- }
- }
- },
-
- /**
- * Handle focus changes on the places list and the current content view.
- */
- updateDetailsPane: function PO_updateDetailsPane() {
- if (!ContentArea.currentViewOptions.showDetailsPane)
- return;
- let view = PlacesUIUtils.getViewForNode(document.activeElement);
- if (view) {
- let selectedNodes = view.selectedNode ?
- [view.selectedNode] : view.selectedNodes;
- this._fillDetailsPane(selectedNodes);
- }
- },
-
- openFlatContainer: function PO_openFlatContainerFlatContainer(aContainer) {
- if (aContainer.itemId != -1)
- this._places.selectItems([aContainer.itemId]);
- else if (PlacesUtils.nodeIsQuery(aContainer))
- this._places.selectPlaceURI(aContainer.uri);
- },
-
- /**
- * Returns the options associated with the query currently loaded in the
- * main places pane.
- */
- getCurrentOptions: function PO_getCurrentOptions() {
- return PlacesUtils.asQuery(ContentArea.currentView.result.root).queryOptions;
- },
-
- /**
- * Returns the queries associated with the query currently loaded in the
- * main places pane.
- */
- getCurrentQueries: function PO_getCurrentQueries() {
- return PlacesUtils.asQuery(ContentArea.currentView.result.root).getQueries();
- },
-
- /**
- * Open a file-picker and import the selected file into the bookmarks store
- */
- importFromFile: function PO_importFromFile() {
- let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
- let fpCallback = function fpCallback_done(aResult) {
- if (aResult != Ci.nsIFilePicker.returnCancel && fp.fileURL) {
- Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
- BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false)
- .then(null, Components.utils.reportError);
- }
- };
-
- fp.init(window, PlacesUIUtils.getString("SelectImport"),
- Ci.nsIFilePicker.modeOpen);
- fp.appendFilters(Ci.nsIFilePicker.filterHTML);
- fp.open(fpCallback);
- },
-
- /**
- * Allows simple exporting of bookmarks.
- */
- exportBookmarks: function PO_exportBookmarks() {
- let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
- let fpCallback = function fpCallback_done(aResult) {
- if (aResult != Ci.nsIFilePicker.returnCancel) {
- Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
- BookmarkHTMLUtils.exportToFile(fp.file.path)
- .then(null, Components.utils.reportError);
- }
- };
-
- fp.init(window, PlacesUIUtils.getString("EnterExport"),
- Ci.nsIFilePicker.modeSave);
- fp.appendFilters(Ci.nsIFilePicker.filterHTML);
- fp.defaultString = "bookmarks.html";
- fp.open(fpCallback);
- },
-
- /**
- * Populates the restore menu with the dates of the backups available.
- */
- populateRestoreMenu: function PO_populateRestoreMenu() {
- let restorePopup = document.getElementById("fileRestorePopup");
-
- let dateSvc = Cc["@mozilla.org/intl/scriptabledateformat;1"].
- getService(Ci.nsIScriptableDateFormat);
-
- // Remove existing menu items. Last item is the restoreFromFile item.
- while (restorePopup.childNodes.length > 1)
- restorePopup.removeChild(restorePopup.firstChild);
-
- Task.spawn(function() {
- let backupFiles = yield PlacesBackups.getBackupFiles();
- if (backupFiles.length == 0)
- return;
-
- // Populate menu with backups.
- for (let i = 0; i < backupFiles.length; i++) {
- let fileSize = (yield OS.File.stat(backupFiles[i])).size;
- let [size, unit] = DownloadUtils.convertByteUnits(fileSize);
- let sizeString = PlacesUtils.getFormattedString("backupFileSizeText",
- [size, unit]);
- let sizeInfo;
- let bookmarkCount = PlacesBackups.getBookmarkCountForFile(backupFiles[i]);
- if (bookmarkCount != null) {
- sizeInfo = " (" + sizeString + " - " +
- PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
- bookmarkCount,
- [bookmarkCount]) +
- ")";
- } else {
- sizeInfo = " (" + sizeString + ")";
- }
-
- let backupDate = PlacesBackups.getDateForFile(backupFiles[i]);
- let m = restorePopup.insertBefore(document.createElement("menuitem"),
- document.getElementById("restoreFromFile"));
- m.setAttribute("label",
- dateSvc.FormatDate("",
- Ci.nsIScriptableDateFormat.dateFormatLong,
- backupDate.getFullYear(),
- backupDate.getMonth() + 1,
- backupDate.getDate()) +
- sizeInfo);
- m.setAttribute("value", OS.Path.basename(backupFiles[i]));
- m.setAttribute("oncommand",
- "PlacesOrganizer.onRestoreMenuItemClick(this);");
- }
-
- // Add the restoreFromFile item.
- restorePopup.insertBefore(document.createElement("menuseparator"),
- document.getElementById("restoreFromFile"));
- });
- },
-
- /**
- * Called when a menuitem is selected from the restore menu.
- */
- onRestoreMenuItemClick: function PO_onRestoreMenuItemClick(aMenuItem) {
- Task.spawn(function() {
- let backupName = aMenuItem.getAttribute("value");
- let backupFilePaths = yield PlacesBackups.getBackupFiles();
- for (let backupFilePath of backupFilePaths) {
- if (OS.Path.basename(backupFilePath) == backupName) {
- PlacesOrganizer.restoreBookmarksFromFile(new FileUtils.File(backupFilePath));
- break;
- }
- }
- });
- },
-
- /**
- * Called when 'Choose File...' is selected from the restore menu.
- * Prompts for a file and restores bookmarks to those in the file.
- */
- onRestoreBookmarksFromFile: function PO_onRestoreBookmarksFromFile() {
- let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
- getService(Ci.nsIProperties);
- let backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
- let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
- let fpCallback = function fpCallback_done(aResult) {
- if (aResult != Ci.nsIFilePicker.returnCancel) {
- this.restoreBookmarksFromFile(fp.file);
- }
- }.bind(this);
-
- fp.init(window, PlacesUIUtils.getString("bookmarksRestoreTitle"),
- Ci.nsIFilePicker.modeOpen);
- fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"),
- RESTORE_FILEPICKER_FILTER_EXT);
- fp.appendFilters(Ci.nsIFilePicker.filterAll);
- fp.displayDirectory = backupsDir;
- fp.open(fpCallback);
- },
-
- /**
- * Restores bookmarks from a JSON file.
- */
- restoreBookmarksFromFile: function PO_restoreBookmarksFromFile(aFile) {
- // check file extension
- let filePath = aFile.path;
- if (!filePath.toLowerCase().endsWith("json") &&
- !filePath.toLowerCase().endsWith("jsonlz4")) {
- this._showErrorAlert(PlacesUIUtils.getString("bookmarksRestoreFormatError"));
- return;
- }
-
- // confirm ok to delete existing bookmarks
- var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].
- getService(Ci.nsIPromptService);
- if (!prompts.confirm(null,
- PlacesUIUtils.getString("bookmarksRestoreAlertTitle"),
- PlacesUIUtils.getString("bookmarksRestoreAlert")))
- return;
-
- Task.spawn(function() {
- try {
- yield BookmarkJSONUtils.importFromFile(aFile.path, true);
- } catch(ex) {
- PlacesOrganizer._showErrorAlert(PlacesUIUtils.getString("bookmarksRestoreParseError"));
- }
- });
- },
-
- _showErrorAlert: function PO__showErrorAlert(aMsg) {
- var brandShortName = document.getElementById("brandStrings").
- getString("brandShortName");
-
- Cc["@mozilla.org/embedcomp/prompt-service;1"].
- getService(Ci.nsIPromptService).
- alert(window, brandShortName, aMsg);
- },
-
- /**
- * Backup bookmarks to desktop, auto-generate a filename with a date.
- * The file is a JSON serialization of bookmarks, tags and any annotations
- * of those items.
- */
- backupBookmarks: function PO_backupBookmarks() {
- let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
- getService(Ci.nsIProperties);
- let backupsDir = dirSvc.get("Desk", Ci.nsILocalFile);
- let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
- let fpCallback = function fpCallback_done(aResult) {
- if (aResult != Ci.nsIFilePicker.returnCancel) {
- BookmarkJSONUtils.exportToFile(fp.file.path);
- }
- };
-
- fp.init(window, PlacesUIUtils.getString("bookmarksBackupTitle"),
- Ci.nsIFilePicker.modeSave);
- fp.appendFilter(PlacesUIUtils.getString("bookmarksRestoreFilterName"),
- RESTORE_FILEPICKER_FILTER_EXT);
- fp.defaultString = PlacesBackups.getFilenameForDate();
- fp.displayDirectory = backupsDir;
- fp.open(fpCallback);
- },
-
- _paneDisabled: false,
- _setDetailsFieldsDisabledState:
- function PO__setDetailsFieldsDisabledState(aDisabled) {
- if (aDisabled) {
- document.getElementById("paneElementsBroadcaster")
- .setAttribute("disabled", "true");
- }
- else {
- document.getElementById("paneElementsBroadcaster")
- .removeAttribute("disabled");
- }
- },
-
- _detectAndSetDetailsPaneMinimalState:
- function PO__detectAndSetDetailsPaneMinimalState(aNode) {
- /**
- * The details of simple folder-items (as opposed to livemarks) or the
- * of livemark-children are not likely to fill the infoBox anyway,
- * thus we remove the "More/Less" button and show all details.
- *
- * the wasminimal attribute here is used to persist the "more/less"
- * state in a bookmark->folder->bookmark scenario.
- */
- var infoBox = document.getElementById("infoBox");
- var infoBoxExpander = document.getElementById("infoBoxExpander");
- var infoBoxExpanderWrapper = document.getElementById("infoBoxExpanderWrapper");
- var additionalInfoBroadcaster = document.getElementById("additionalInfoBroadcaster");
-
- if (!aNode) {
- infoBoxExpanderWrapper.hidden = true;
- return;
- }
- if (aNode.itemId != -1 &&
- PlacesUtils.nodeIsFolder(aNode) && !aNode._feedURI) {
- if (infoBox.getAttribute("minimal") == "true")
- infoBox.setAttribute("wasminimal", "true");
- infoBox.removeAttribute("minimal");
- infoBoxExpanderWrapper.hidden = true;
- }
- else {
- if (infoBox.getAttribute("wasminimal") == "true")
- infoBox.setAttribute("minimal", "true");
- infoBox.removeAttribute("wasminimal");
- infoBoxExpanderWrapper.hidden =
- this._additionalInfoFields.every(function (id)
- document.getElementById(id).collapsed);
- }
- additionalInfoBroadcaster.hidden = infoBox.getAttribute("minimal") == "true";
- },
-
- // NOT YET USED
- updateThumbnailProportions: function PO_updateThumbnailProportions() {
- var previewBox = document.getElementById("previewBox");
- var canvas = document.getElementById("itemThumbnail");
- var height = previewBox.boxObject.height;
- var width = height * (screen.width / screen.height);
- canvas.width = width;
- canvas.height = height;
- },
-
- _fillDetailsPane: function PO__fillDetailsPane(aNodeList) {
- var infoBox = document.getElementById("infoBox");
- var detailsDeck = document.getElementById("detailsDeck");
-
- // Make sure the infoBox UI is visible if we need to use it, we hide it
- // below when we don't.
- infoBox.hidden = false;
- var aSelectedNode = aNodeList.length == 1 ? aNodeList[0] : null;
- // If a textbox within a panel is focused, force-blur it so its contents
- // are saved
- if (gEditItemOverlay.itemId != -1) {
- var focusedElement = document.commandDispatcher.focusedElement;
- if ((focusedElement instanceof HTMLInputElement ||
- focusedElement instanceof HTMLTextAreaElement) &&
- /^editBMPanel.*/.test(focusedElement.parentNode.parentNode.id))
- focusedElement.blur();
-
- // don't update the panel if we are already editing this node unless we're
- // in multi-edit mode
- if (aSelectedNode) {
- var concreteId = PlacesUtils.getConcreteItemId(aSelectedNode);
- var nodeIsSame = gEditItemOverlay.itemId == aSelectedNode.itemId ||
- gEditItemOverlay.itemId == concreteId ||
- (aSelectedNode.itemId == -1 && gEditItemOverlay.uri &&
- gEditItemOverlay.uri == aSelectedNode.uri);
- if (nodeIsSame && detailsDeck.selectedIndex == 1 &&
- !gEditItemOverlay.multiEdit)
- return;
- }
- }
-
- // Clean up the panel before initing it again.
- gEditItemOverlay.uninitPanel(false);
-
- if (aSelectedNode && !PlacesUtils.nodeIsSeparator(aSelectedNode)) {
- detailsDeck.selectedIndex = 1;
- // Using the concrete itemId is arguably wrong. The bookmarks API
- // does allow setting properties for folder shortcuts as well, but since
- // the UI does not distinct between the couple, we better just show
- // the concrete item properties for shortcuts to root nodes.
- var concreteId = PlacesUtils.getConcreteItemId(aSelectedNode);
- var isRootItem = concreteId != -1 && PlacesUtils.isRootItem(concreteId);
- var readOnly = isRootItem ||
- aSelectedNode.parent.itemId == PlacesUIUtils.leftPaneFolderId;
- var useConcreteId = isRootItem ||
- PlacesUtils.nodeIsTagQuery(aSelectedNode);
- var itemId = -1;
- if (concreteId != -1 && useConcreteId)
- itemId = concreteId;
- else if (aSelectedNode.itemId != -1)
- itemId = aSelectedNode.itemId;
- else
- itemId = PlacesUtils._uri(aSelectedNode.uri);
-
- gEditItemOverlay.initPanel(itemId, { hiddenRows: ["folderPicker"]
- , forceReadOnly: readOnly
- , titleOverride: aSelectedNode.title
- });
-
- // Dynamically generated queries, like history date containers, have
- // itemId !=0 and do not exist in history. For them the panel is
- // read-only, but empty, since it can't get a valid title for the object.
- // In such a case we force the title using the selectedNode one, for UI
- // polishness.
- if (aSelectedNode.itemId == -1 &&
- (PlacesUtils.nodeIsDay(aSelectedNode) ||
- PlacesUtils.nodeIsHost(aSelectedNode)))
- gEditItemOverlay._element("namePicker").value = aSelectedNode.title;
-
- this._detectAndSetDetailsPaneMinimalState(aSelectedNode);
- }
- else if (!aSelectedNode && aNodeList[0]) {
- var itemIds = [];
- for (var i = 0; i < aNodeList.length; i++) {
- if (!PlacesUtils.nodeIsBookmark(aNodeList[i]) &&
- !PlacesUtils.nodeIsURI(aNodeList[i])) {
- detailsDeck.selectedIndex = 0;
- var selectItemDesc = document.getElementById("selectItemDescription");
- var itemsCountLabel = document.getElementById("itemsCountText");
- selectItemDesc.hidden = false;
- itemsCountLabel.value =
- PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
- aNodeList.length, [aNodeList.length]);
- infoBox.hidden = true;
- return;
- }
- itemIds[i] = aNodeList[i].itemId != -1 ? aNodeList[i].itemId :
- PlacesUtils._uri(aNodeList[i].uri);
- }
- detailsDeck.selectedIndex = 1;
- gEditItemOverlay.initPanel(itemIds,
- { hiddenRows: ["folderPicker",
- "loadInSidebar",
- "location",
- "keyword",
- "description",
- "name"]});
- this._detectAndSetDetailsPaneMinimalState(aSelectedNode);
- }
- else {
- detailsDeck.selectedIndex = 0;
- infoBox.hidden = true;
- let selectItemDesc = document.getElementById("selectItemDescription");
- let itemsCountLabel = document.getElementById("itemsCountText");
- let itemsCount = 0;
- if (ContentArea.currentView.result) {
- let rootNode = ContentArea.currentView.result.root;
- if (rootNode.containerOpen)
- itemsCount = rootNode.childCount;
- }
- if (itemsCount == 0) {
- selectItemDesc.hidden = true;
- itemsCountLabel.value = PlacesUIUtils.getString("detailsPane.noItems");
- }
- else {
- selectItemDesc.hidden = false;
- itemsCountLabel.value =
- PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
- itemsCount, [itemsCount]);
- }
- }
- },
-
- // NOT YET USED
- _updateThumbnail: function PO__updateThumbnail() {
- var bo = document.getElementById("previewBox").boxObject;
- var width = bo.width;
- var height = bo.height;
-
- var canvas = document.getElementById("itemThumbnail");
- var ctx = canvas.getContext('2d');
- var notAvailableText = canvas.getAttribute("notavailabletext");
- ctx.save();
- ctx.fillStyle = "-moz-Dialog";
- ctx.fillRect(0, 0, width, height);
- ctx.translate(width/2, height/2);
-
- ctx.fillStyle = "GrayText";
- ctx.mozTextStyle = "12pt sans serif";
- var len = ctx.mozMeasureText(notAvailableText);
- ctx.translate(-len/2,0);
- ctx.mozDrawText(notAvailableText);
- ctx.restore();
- },
-
- toggleAdditionalInfoFields: function PO_toggleAdditionalInfoFields() {
- var infoBox = document.getElementById("infoBox");
- var infoBoxExpander = document.getElementById("infoBoxExpander");
- var infoBoxExpanderLabel = document.getElementById("infoBoxExpanderLabel");
- var additionalInfoBroadcaster = document.getElementById("additionalInfoBroadcaster");
-
- if (infoBox.getAttribute("minimal") == "true") {
- infoBox.removeAttribute("minimal");
- infoBoxExpanderLabel.value = infoBoxExpanderLabel.getAttribute("lesslabel");
- infoBoxExpanderLabel.accessKey = infoBoxExpanderLabel.getAttribute("lessaccesskey");
- infoBoxExpander.className = "expander-up";
- additionalInfoBroadcaster.removeAttribute("hidden");
- }
- else {
- infoBox.setAttribute("minimal", "true");
- infoBoxExpanderLabel.value = infoBoxExpanderLabel.getAttribute("morelabel");
- infoBoxExpanderLabel.accessKey = infoBoxExpanderLabel.getAttribute("moreaccesskey");
- infoBoxExpander.className = "expander-down";
- additionalInfoBroadcaster.setAttribute("hidden", "true");
- }
- },
-
- /**
- * Save the current search (or advanced query) to the bookmarks root.
- */
- saveSearch: function PO_saveSearch() {
- // Get the place: uri for the query.
- // If the advanced query builder is showing, use that.
- var options = this.getCurrentOptions();
- var queries = this.getCurrentQueries();
-
- var placeSpec = PlacesUtils.history.queriesToQueryString(queries,
- queries.length,
- options);
- var placeURI = Cc["@mozilla.org/network/io-service;1"].
- getService(Ci.nsIIOService).
- newURI(placeSpec, null, null);
-
- // Prompt the user for a name for the query.
- // XXX - using prompt service for now; will need to make
- // a real dialog and localize when we're sure this is the UI we want.
- var title = PlacesUIUtils.getString("saveSearch.title");
- var inputLabel = PlacesUIUtils.getString("saveSearch.inputLabel");
- var defaultText = PlacesUIUtils.getString("saveSearch.inputDefaultText");
-
- var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].
- getService(Ci.nsIPromptService);
- var check = {value: false};
- var input = {value: defaultText};
- var save = prompts.prompt(null, title, inputLabel, input, null, check);
-
- // Don't add the query if the user cancels or clears the seach name.
- if (!save || input.value == "")
- return;
-
- // Add the place: uri as a bookmark under the bookmarks root.
- var txn = new PlacesCreateBookmarkTransaction(placeURI,
- PlacesUtils.bookmarksMenuFolderId,
- PlacesUtils.bookmarks.DEFAULT_INDEX,
- input.value);
- PlacesUtils.transactionManager.doTransaction(txn);
-
- // select and load the new query
- this._places.selectPlaceURI(placeSpec);
- }
-};
-
-/**
- * A set of utilities relating to search within Bookmarks and History.
- */
-var PlacesSearchBox = {
-
- /**
- * The Search text field
- */
- get searchFilter() {
- return document.getElementById("searchFilter");
- },
-
- /**
- * Folders to include when searching.
- */
- _folders: [],
- get folders() {
- if (this._folders.length == 0) {
- this._folders.push(PlacesUtils.bookmarksMenuFolderId,
- PlacesUtils.unfiledBookmarksFolderId,
- PlacesUtils.toolbarFolderId);
- }
- return this._folders;
- },
- set folders(aFolders) {
- this._folders = aFolders;
- return aFolders;
- },
-
- /**
- * Run a search for the specified text, over the collection specified by
- * the dropdown arrow. The default is all bookmarks, but can be
- * localized to the active collection.
- * @param filterString
- * The text to search for.
- */
- search: function PSB_search(filterString) {
- var PO = PlacesOrganizer;
- // If the user empties the search box manually, reset it and load all
- // contents of the current scope.
- // XXX this might be to jumpy, maybe should search for "", so results
- // are ungrouped, and search box not reset
- if (filterString == "") {
- PO.onPlaceSelected(false);
- return;
- }
-
- let currentView = ContentArea.currentView;
- let currentOptions = PO.getCurrentOptions();
-
- // Search according to the current scope and folders, which were set by
- // PQB_setScope()
- switch (PlacesSearchBox.filterCollection) {
- case "collection":
- currentView.applyFilter(filterString, this.folders);
- break;
- case "bookmarks":
- currentView.applyFilter(filterString, this.folders);
- break;
- case "history":
- if (currentOptions.queryType != Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
- var query = PlacesUtils.history.getNewQuery();
- query.searchTerms = filterString;
- var options = currentOptions.clone();
- // Make sure we're getting uri results.
- options.resultType = currentOptions.RESULTS_AS_URI;
- options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
- options.includeHidden = true;
- currentView.load([query], options);
- }
- else {
- currentView.applyFilter(filterString, null, true);
- }
- break;
- case "downloads":
- if (currentView == ContentTree.view) {
- let query = PlacesUtils.history.getNewQuery();
- query.searchTerms = filterString;
- query.setTransitions([Ci.nsINavHistoryService.TRANSITION_DOWNLOAD], 1);
- let options = currentOptions.clone();
- // Make sure we're getting uri results.
- options.resultType = currentOptions.RESULTS_AS_URI;
- options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
- options.includeHidden = true;
- currentView.load([query], options);
- }
- else {
- // The new downloads view doesn't use places for searching downloads.
- currentView.searchTerm = filterString;
- }
- break;
- default:
- throw new Components.Exception("Invalid filterCollection on search",
- Components.results.NS_ERROR_INVALID_ARG);
- }
-
- PlacesSearchBox.showSearchUI();
-
- // Update the details panel
- PlacesOrganizer.updateDetailsPane();
- },
-
- /**
- * Finds across all history, downloads or all bookmarks.
- */
- findAll: function PSB_findAll() {
- switch (this.filterCollection) {
- case "history":
- PlacesQueryBuilder.setScope("history");
- break;
- case "downloads":
- PlacesQueryBuilder.setScope("downloads");
- break;
- default:
- PlacesQueryBuilder.setScope("bookmarks");
- break;
- }
- this.focus();
- },
-
- /**
- * Updates the display with the title of the current collection.
- * @param aTitle
- * The title of the current collection.
- */
- updateCollectionTitle: function PSB_updateCollectionTitle(aTitle) {
- let title = "";
- // This is needed when a user performs a folder-specific search
- // using the scope bar, removes the search-string, and unfocuses
- // the search box, at least until the removal of the scope bar.
- if (aTitle) {
- title = PlacesUIUtils.getFormattedString("searchCurrentDefault",
- [aTitle]);
- }
- else {
- switch (this.filterCollection) {
- case "history":
- title = PlacesUIUtils.getString("searchHistory");
- break;
- case "downloads":
- title = PlacesUIUtils.getString("searchDownloads");
- break;
- default:
- title = PlacesUIUtils.getString("searchBookmarks");
- }
- }
- this.searchFilter.placeholder = title;
- },
-
- /**
- * Gets/sets the active collection from the dropdown menu.
- */
- get filterCollection() {
- return this.searchFilter.getAttribute("collection");
- },
- set filterCollection(collectionName) {
- if (collectionName == this.filterCollection)
- return collectionName;
-
- this.searchFilter.setAttribute("collection", collectionName);
-
- var newGrayText = null;
- if (collectionName == "collection") {
- newGrayText = PlacesOrganizer._places.selectedNode.title ||
- document.getElementById("scopeBarFolder").
- getAttribute("emptytitle");
- }
- this.updateCollectionTitle(newGrayText);
- return collectionName;
- },
-
- /**
- * Focus the search box
- */
- focus: function PSB_focus() {
- this.searchFilter.focus();
- },
-
- /**
- * Set up the gray text in the search bar as the Places View loads.
- */
- init: function PSB_init() {
- this.updateCollectionTitle();
- },
-
- /**
- * Gets or sets the text shown in the Places Search Box
- */
- get value() {
- return this.searchFilter.value;
- },
- set value(value) {
- return this.searchFilter.value = value;
- },
-
- showSearchUI: function PSB_showSearchUI() {
- // Hide the advanced search controls when the user hasn't searched
- var searchModifiers = document.getElementById("searchModifiers");
- searchModifiers.hidden = false;
- },
-
- hideSearchUI: function PSB_hideSearchUI() {
- var searchModifiers = document.getElementById("searchModifiers");
- searchModifiers.hidden = true;
- }
-};
-
-/**
- * Functions and data for advanced query builder
- */
-var PlacesQueryBuilder = {
-
- queries: [],
- queryOptions: null,
-
- /**
- * Called when a scope button in the scope bar is clicked.
- * @param aButton
- * the scope button that was selected
- */
- onScopeSelected: function PQB_onScopeSelected(aButton) {
- switch (aButton.id) {
- case "scopeBarHistory":
- this.setScope("history");
- break;
- case "scopeBarFolder":
- this.setScope("collection");
- break;
- case "scopeBarDownloads":
- this.setScope("downloads");
- break;
- case "scopeBarAll":
- this.setScope("bookmarks");
- break;
- default:
- throw new Components.Exception("Invalid search scope button ID",
- Components.results.NS_ERROR_INVALID_ARG);
- break;
- }
- },
-
- /**
- * Sets the search scope. This can be called when no search is active, and
- * in that case, when the user does begin a search aScope will be used (see
- * PSB_search()). If there is an active search, it's performed again to
- * update the content tree.
- * @param aScope
- * The search scope: "bookmarks", "collection", "downloads" or
- * "history".
- */
- setScope: function PQB_setScope(aScope) {
- // Determine filterCollection, folders, and scopeButtonId based on aScope.
- var filterCollection;
- var folders = [];
- var scopeButtonId;
- switch (aScope) {
- case "history":
- filterCollection = "history";
- scopeButtonId = "scopeBarHistory";
- break;
- case "collection":
- // The folder scope button can only become hidden upon selecting a new
- // folder in the left pane, and the disabled state will remain unchanged
- // until a new folder is selected. See PO__setScopeForNode().
- if (!document.getElementById("scopeBarFolder").hidden) {
- filterCollection = "collection";
- scopeButtonId = "scopeBarFolder";
- folders.push(PlacesUtils.getConcreteItemId(
- PlacesOrganizer._places.selectedNode));
- break;
- }
- // Fall through. If collection scope doesn't make sense for the
- // selected node, choose bookmarks scope.
- case "bookmarks":
- filterCollection = "bookmarks";
- scopeButtonId = "scopeBarAll";
- folders.push(PlacesUtils.bookmarksMenuFolderId,
- PlacesUtils.toolbarFolderId,
- PlacesUtils.unfiledBookmarksFolderId);
- break;
- case "downloads":
- filterCollection = "downloads";
- scopeButtonId = "scopeBarDownloads";
- break;
- default:
- throw new Components.Exception("Invalid search scope",
- Components.results.NS_ERROR_INVALID_ARG);
- break;
- }
-
- // Check the appropriate scope button in the scope bar.
- document.getElementById(scopeButtonId).checked = true;
-
- // Update the search box. Re-search if there's an active search.
- PlacesSearchBox.filterCollection = filterCollection;
- PlacesSearchBox.folders = folders;
- var searchStr = PlacesSearchBox.searchFilter.value;
- if (searchStr)
- PlacesSearchBox.search(searchStr);
- }
-};
-
-/**
- * Population and commands for the View Menu.
- */
-var ViewMenu = {
- /**
- * Removes content generated previously from a menupopup.
- * @param popup
- * The popup that contains the previously generated content.
- * @param startID
- * The id attribute of an element that is the start of the
- * dynamically generated region - remove elements after this
- * item only.
- * Must be contained by popup. Can be null (in which case the
- * contents of popup are removed).
- * @param endID
- * The id attribute of an element that is the end of the
- * dynamically generated region - remove elements up to this
- * item only.
- * Must be contained by popup. Can be null (in which case all
- * items until the end of the popup will be removed). Ignored
- * if startID is null.
- * @returns The element for the caller to insert new items before,
- * null if the caller should just append to the popup.
- */
- _clean: function VM__clean(popup, startID, endID) {
- if (endID)
- NS_ASSERT(startID, "meaningless to have valid endID and null startID");
- if (startID) {
- var startElement = document.getElementById(startID);
- NS_ASSERT(startElement.parentNode ==
- popup, "startElement is not in popup");
- NS_ASSERT(startElement,
- "startID does not correspond to an existing element");
- var endElement = null;
- if (endID) {
- endElement = document.getElementById(endID);
- NS_ASSERT(endElement.parentNode == popup,
- "endElement is not in popup");
- NS_ASSERT(endElement,
- "endID does not correspond to an existing element");
- }
- while (startElement.nextSibling != endElement)
- popup.removeChild(startElement.nextSibling);
- return endElement;
- }
- else {
- while(popup.hasChildNodes())
- popup.removeChild(popup.firstChild);
- }
- return null;
- },
-
- /**
- * Fills a menupopup with a list of columns
- * @param event
- * The popupshowing event that invoked this function.
- * @param startID
- * see _clean
- * @param endID
- * see _clean
- * @param type
- * the type of the menuitem, e.g. "radio" or "checkbox".
- * Can be null (no-type).
- * Checkboxes are checked if the column is visible.
- * @param propertyPrefix
- * If propertyPrefix is non-null:
- * propertyPrefix + column ID + ".label" will be used to get the
- * localized label string.
- * propertyPrefix + column ID + ".accesskey" will be used to get the
- * localized accesskey.
- * If propertyPrefix is null, the column label is used as label and
- * no accesskey is assigned.
- */
- fillWithColumns: function VM_fillWithColumns(event, startID, endID, type, propertyPrefix) {
- var popup = event.target;
- var pivot = this._clean(popup, startID, endID);
-
- // If no column is "sort-active", the "Unsorted" item needs to be checked,
- // so track whether or not we find a column that is sort-active.
- var isSorted = false;
- var content = document.getElementById("placeContent");
- var columns = content.columns;
- for (var i = 0; i < columns.count; ++i) {
- var column = columns.getColumnAt(i).element;
- if (popup.parentNode && (popup.parentNode.id == "viewSort")) {
- switch (column.id) {
- case "placesContentParentFolder":
- continue;
- case "placesContentParentFolderPath":
- continue;
- }
- }
- var menuitem = document.createElement("menuitem");
- menuitem.id = "menucol_" + column.id;
- menuitem.column = column;
- var label = column.getAttribute("label");
- if (propertyPrefix) {
- var menuitemPrefix = propertyPrefix;
- // for string properties, use "name" as the id, instead of "title"
- // see bug #386287 for details
- var columnId = column.getAttribute("anonid");
- menuitemPrefix += columnId == "title" ? "name" : columnId;
- label = PlacesUIUtils.getString(menuitemPrefix + ".label");
- var accesskey = PlacesUIUtils.getString(menuitemPrefix + ".accesskey");
- menuitem.setAttribute("accesskey", accesskey);
- }
- menuitem.setAttribute("label", label);
- if (type == "radio") {
- menuitem.setAttribute("type", "radio");
- menuitem.setAttribute("name", "columns");
- // This column is the sort key. Its item is checked.
- if (column.getAttribute("sortDirection") != "") {
- menuitem.setAttribute("checked", "true");
- isSorted = true;
- }
- }
- else if (type == "checkbox") {
- menuitem.setAttribute("type", "checkbox");
- // Cannot uncheck the primary column.
- if (column.getAttribute("primary") == "true")
- menuitem.setAttribute("disabled", "true");
- // Items for visible columns are checked.
- if (!column.hidden)
- menuitem.setAttribute("checked", "true");
- }
- if (pivot)
- popup.insertBefore(menuitem, pivot);
- else
- popup.appendChild(menuitem);
- }
- event.stopPropagation();
- },
-
- /**
- * Set up the content of the view menu.
- */
- populateSortMenu: function VM_populateSortMenu(event) {
- this.fillWithColumns(event, "viewUnsorted", "directionSeparator", "radio", "view.sortBy.");
-
- var sortColumn = this._getSortColumn();
- var viewSortAscending = document.getElementById("viewSortAscending");
- var viewSortDescending = document.getElementById("viewSortDescending");
- // We need to remove an existing checked attribute because the unsorted
- // menu item is not rebuilt every time we open the menu like the others.
- var viewUnsorted = document.getElementById("viewUnsorted");
- if (!sortColumn) {
- viewSortAscending.removeAttribute("checked");
- viewSortDescending.removeAttribute("checked");
- viewUnsorted.setAttribute("checked", "true");
- }
- else if (sortColumn.getAttribute("sortDirection") == "ascending") {
- viewSortAscending.setAttribute("checked", "true");
- viewSortDescending.removeAttribute("checked");
- viewUnsorted.removeAttribute("checked");
- }
- else if (sortColumn.getAttribute("sortDirection") == "descending") {
- viewSortDescending.setAttribute("checked", "true");
- viewSortAscending.removeAttribute("checked");
- viewUnsorted.removeAttribute("checked");
- }
- },
-
- /**
- * Shows/Hides a tree column.
- * @param element
- * The menuitem element for the column
- */
- showHideColumn: function VM_showHideColumn(element) {
- var column = element.column;
-
- var splitter = column.nextSibling;
- if (splitter && splitter.localName != "splitter")
- splitter = null;
-
- if (element.getAttribute("checked") == "true") {
- column.setAttribute("hidden", "false");
- if (splitter)
- splitter.removeAttribute("hidden");
- }
- else {
- column.setAttribute("hidden", "true");
- if (splitter)
- splitter.setAttribute("hidden", "true");
- }
- },
-
- /**
- * Gets the last column that was sorted.
- * @returns the currently sorted column, null if there is no sorted column.
- */
- _getSortColumn: function VM__getSortColumn() {
- var content = document.getElementById("placeContent");
- var cols = content.columns;
- for (var i = 0; i < cols.count; ++i) {
- var column = cols.getColumnAt(i).element;
- var sortDirection = column.getAttribute("sortDirection");
- if (sortDirection == "ascending" || sortDirection == "descending")
- return column;
- }
- return null;
- },
-
- /**
- * Sorts the view by the specified column.
- * @param aColumn
- * The colum that is the sort key. Can be null - the
- * current sort column or the title column will be used.
- * @param aDirection
- * The direction to sort - "ascending" or "descending".
- * Can be null - the last direction or descending will be used.
- *
- * If both aColumnID and aDirection are null, the view will be unsorted.
- */
- setSortColumn: function VM_setSortColumn(aColumn, aDirection) {
- var result = document.getElementById("placeContent").result;
- if (!aColumn && !aDirection) {
- result.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
- return;
- }
-
- var columnId;
- if (aColumn) {
- columnId = aColumn.getAttribute("anonid");
- if (!aDirection) {
- var sortColumn = this._getSortColumn();
- if (sortColumn)
- aDirection = sortColumn.getAttribute("sortDirection");
- }
- }
- else {
- var sortColumn = this._getSortColumn();
- columnId = sortColumn ? sortColumn.getAttribute("anonid") : "title";
- }
-
- // This maps the possible values of columnId (i.e., anonid's of treecols in
- // placeContent) to the default sortingMode and sortingAnnotation values for
- // each column.
- // key: Sort key in the name of one of the
- // nsINavHistoryQueryOptions.SORT_BY_* constants
- // dir: Default sort direction to use if none has been specified
- // anno: The annotation to sort by, if key is "ANNOTATION"
- var colLookupTable = {
- title: { key: "TITLE", dir: "ascending" },
- tags: { key: "TAGS", dir: "ascending" },
- url: { key: "URI", dir: "ascending" },
- date: { key: "DATE", dir: "descending" },
- visitCount: { key: "VISITCOUNT", dir: "descending" },
- keyword: { key: "KEYWORD", dir: "ascending" },
- dateAdded: { key: "DATEADDED", dir: "descending" },
- lastModified: { key: "LASTMODIFIED", dir: "descending" },
- description: { key: "ANNOTATION",
- dir: "ascending",
- anno: PlacesUIUtils.DESCRIPTION_ANNO }
- };
-
- // Make sure we have a valid column.
- if (!colLookupTable.hasOwnProperty(columnId))
- throw new Components.Exception("Invalid column",
- Components.results.NS_ERROR_INVALID_ARG);
-
- // Use a default sort direction if none has been specified. If aDirection
- // is invalid, result.sortingMode will be undefined, which has the effect
- // of unsorting the tree.
- aDirection = (aDirection || colLookupTable[columnId].dir).toUpperCase();
-
- var sortConst = "SORT_BY_" + colLookupTable[columnId].key + "_" + aDirection;
- result.sortingAnnotation = colLookupTable[columnId].anno || "";
- result.sortingMode = Ci.nsINavHistoryQueryOptions[sortConst];
- }
-}
-
-var ContentArea = {
- _specialViews: new Map(),
-
- init: function CA_init() {
- this._deck = document.getElementById("placesViewsDeck");
- this._toolbar = document.getElementById("placesToolbar");
- ContentTree.init();
- this._setupView();
- },
-
- /**
- * Gets the content view to be used for loading the given query.
- * If a custom view was set by setContentViewForQueryString, that
- * view would be returned, else the default tree view is returned
- *
- * @param aQueryString
- * a query string
- * @return the view to be used for loading aQueryString.
- */
- getContentViewForQueryString:
- function CA_getContentViewForQueryString(aQueryString) {
- try {
- if (this._specialViews.has(aQueryString)) {
- let { view, options } = this._specialViews.get(aQueryString);
- if (typeof view == "function") {
- view = view();
- this._specialViews.set(aQueryString, { view: view, options: options });
- }
- return view;
- }
- }
- catch(ex) {
- Components.utils.reportError(ex);
- }
- return ContentTree.view;
- },
-
- /**
- * Sets a custom view to be used rather than the default places tree
- * whenever the given query is selected in the left pane.
- * @param aQueryString
- * a query string
- * @param aView
- * Either the custom view or a function that will return the view
- * the first (and only) time it's called.
- * @param [optional] aOptions
- * Object defining special options for the view.
- * @see ContentTree.viewOptions for supported options and default values.
- */
- setContentViewForQueryString:
- function CA_setContentViewForQueryString(aQueryString, aView, aOptions) {
- if (!aQueryString ||
- typeof aView != "object" && typeof aView != "function")
- throw new Components.Exception("Invalid arguments",
- Components.results.NS_ERROR_INVALID_ARG);
-
- this._specialViews.set(aQueryString, { view: aView,
- options: aOptions || new Object() });
- },
-
- get currentView() PlacesUIUtils.getViewForNode(this._deck.selectedPanel),
- set currentView(aNewView) {
- let oldView = this.currentView;
- if (oldView != aNewView) {
- this._deck.selectedPanel = aNewView.associatedElement;
-
- // If the content area inactivated view was focused, move focus
- // to the new view.
- if (document.activeElement == oldView.associatedElement)
- aNewView.associatedElement.focus();
- }
- return aNewView;
- },
-
- get currentPlace() this.currentView.place,
- set currentPlace(aQueryString) {
- let oldView = this.currentView;
- let newView = this.getContentViewForQueryString(aQueryString);
- newView.place = aQueryString;
- if (oldView != newView) {
- oldView.active = false;
- this.currentView = newView;
- this._setupView();
- newView.active = true;
- }
- return aQueryString;
- },
-
- /**
- * Applies view options.
- */
- _setupView: function CA__setupView() {
- let options = this.currentViewOptions;
-
- // showDetailsPane.
- let detailsDeck = document.getElementById("detailsDeck");
- detailsDeck.hidden = !options.showDetailsPane;
-
- // toolbarSet.
- for (let elt of this._toolbar.childNodes) {
- // On Windows and Linux the menu buttons are menus wrapped in a menubar.
- if (elt.id == "placesMenu") {
- for (let menuElt of elt.childNodes) {
- menuElt.hidden = options.toolbarSet.indexOf(menuElt.id) == -1;
- }
- }
- else {
- elt.hidden = options.toolbarSet.indexOf(elt.id) == -1;
- }
- }
- },
-
- /**
- * Options for the current view.
- *
- * @see ContentTree.viewOptions for supported options and default values.
- */
- get currentViewOptions() {
- // Use ContentTree options as default.
- let viewOptions = ContentTree.viewOptions;
- if (this._specialViews.has(this.currentPlace)) {
- let { view, options } = this._specialViews.get(this.currentPlace);
- for (let option in options) {
- viewOptions[option] = options[option];
- }
- }
- return viewOptions;
- },
-
- focus: function() {
- this._deck.selectedPanel.focus();
- }
-};
-
-var ContentTree = {
- init: function CT_init() {
- this._view = document.getElementById("placeContent");
- },
-
- get view() this._view,
-
- get viewOptions() Object.seal({
- showDetailsPane: true,
- toolbarSet: "back-button, forward-button, organizeButton, viewMenu, maintenanceButton, libraryToolbarSpacer, searchFilter"
- }),
-
- openSelectedNode: function CT_openSelectedNode(aEvent) {
- let view = this.view;
- PlacesUIUtils.openNodeWithEvent(view.selectedNode, aEvent, view);
- },
-
- onClick: function CT_onClick(aEvent) {
- let node = this.view.selectedNode;
- if (node) {
- let doubleClick = aEvent.button == 0 && aEvent.detail == 2;
- let middleClick = aEvent.button == 1 && aEvent.detail == 1;
- if (PlacesUtils.nodeIsURI(node) && (doubleClick || middleClick)) {
- // Open associated uri in the browser.
- this.openSelectedNode(aEvent);
- }
- else if (middleClick && PlacesUtils.nodeIsContainer(node)) {
- // The command execution function will take care of seeing if the
- // selection is a folder or a different container type, and will
- // load its contents in tabs.
- PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this.view);
- }
- }
- },
-
- onKeyPress: function CT_onKeyPress(aEvent) {
- if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
- this.openSelectedNode(aEvent);
- }
-};
diff --git a/components/places/content/places.xul b/components/places/content/places.xul
deleted file mode 100644
index 92e8a70..0000000
--- a/components/places/content/places.xul
+++ /dev/null
@@ -1,471 +0,0 @@
-<?xml version="1.0"?>
-
-# 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/.
-
-<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
-<?xml-stylesheet href="chrome://browser/content/places/organizer.css"?>
-
-<?xml-stylesheet href="chrome://global/skin/"?>
-<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
-<?xml-stylesheet href="chrome://browser/skin/places/organizer.css"?>
-
-<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>
-
-#ifdef XP_MACOSX
-<?xul-overlay href="chrome://browser/content/macBrowserOverlay.xul"?>
-#else
-<?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
-<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
-<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
-#endif
-
-<!DOCTYPE window [
-<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
-%placesDTD;
-<!ENTITY % editMenuOverlayDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
-%editMenuOverlayDTD;
-<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
-%browserDTD;
-]>
-
-<window id="places"
- title="&places.library.title;"
- windowtype="Places:Organizer"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- xmlns:html="http://www.w3.org/1999/xhtml"
- onload="PlacesOrganizer.init();"
- onunload="PlacesOrganizer.destroy();"
- width="&places.library.width;" height="&places.library.height;"
- screenX="10" screenY="10"
- toggletoolbar="true"
- persist="width height screenX screenY sizemode">
-
- <script type="application/javascript"
- src="chrome://browser/content/places/places.js"/>
- <script type="application/javascript"
- src="chrome://browser/content/utilityOverlay.js"/>
- <script type="application/javascript"
- src="chrome://browser/content/places/editBookmarkOverlay.js"/>
-
- <stringbundleset id="placesStringSet">
- <stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
- </stringbundleset>
-
-#ifdef XP_MACOSX
-#include ../../../base/content/browserMountPoints.inc
-#else
- <commandset id="editMenuCommands"/>
- <commandset id="placesCommands"/>
-#endif
- <keyset id="placesCommandKeys"/>
-
- <commandset id="organizerCommandSet">
- <command id="OrganizerCommand_find:all"
- oncommand="PlacesSearchBox.findAll();"/>
- <command id="OrganizerCommand_export"
- oncommand="PlacesOrganizer.exportBookmarks();"/>
- <command id="OrganizerCommand_import"
- oncommand="PlacesOrganizer.importFromFile();"/>
- <command id="OrganizerCommand_backup"
- oncommand="PlacesOrganizer.backupBookmarks();"/>
- <command id="OrganizerCommand_restoreFromFile"
- oncommand="PlacesOrganizer.onRestoreBookmarksFromFile();"/>
- <command id="OrganizerCommand_search:save"
- oncommand="PlacesOrganizer.saveSearch();"/>
- <command id="OrganizerCommand_search:moreCriteria"
- oncommand="PlacesQueryBuilder.addRow();"/>
- <command id="OrganizerCommand:Back"
- oncommand="PlacesOrganizer.back();"/>
- <command id="OrganizerCommand:Forward"
- oncommand="PlacesOrganizer.forward();"/>
- </commandset>
-
- <keyset id="placesOrganizerKeyset">
- <!-- Instantiation Keys -->
- <key id="placesKey_close" key="&cmd.close.key;" modifiers="accel"
- oncommand="close();"/>
-
- <!-- Command Keys -->
- <key id="placesKey_find:all"
- command="OrganizerCommand_find:all"
- key="&cmd.find.key;"
- modifiers="accel"/>
-
- <!-- Back/Forward Keys Support -->
-#ifndef XP_MACOSX
- <key id="placesKey_goBackKb"
- keycode="VK_LEFT"
- command="OrganizerCommand:Back"
- modifiers="alt"/>
- <key id="placesKey_goForwardKb"
- keycode="VK_RIGHT"
- command="OrganizerCommand:Forward"
- modifiers="alt"/>
-#else
- <key id="placesKey_goBackKb"
- keycode="VK_LEFT"
- command="OrganizerCommand:Back"
- modifiers="accel"/>
- <key id="placesKey_goForwardKb"
- keycode="VK_RIGHT"
- command="OrganizerCommand:Forward"
- modifiers="accel"/>
-#endif
-#ifdef XP_UNIX
- <key id="placesKey_goBackKb2"
- key="&goBackCmd.commandKey;"
- command="OrganizerCommand:Back"
- modifiers="accel"/>
- <key id="placesKey_goForwardKb2"
- key="&goForwardCmd.commandKey;"
- command="OrganizerCommand:Forward"
- modifiers="accel"/>
-#endif
- </keyset>
-
- <keyset id="editMenuKeys">
-#ifdef XP_MACOSX
- <key id="key_delete2" keycode="VK_BACK" command="cmd_delete"/>
-#endif
- </keyset>
-
- <popupset id="placesPopupset">
- <menupopup id="placesContext"/>
- <menupopup id="placesColumnsContext"
- onpopupshowing="ViewMenu.fillWithColumns(event, null, null, 'checkbox', null);"
- oncommand="ViewMenu.showHideColumn(event.target); event.stopPropagation();"/>
- </popupset>
-
- <toolbox id="placesToolbox">
- <toolbar class="chromeclass-toolbar" id="placesToolbar" align="center">
- <toolbarbutton id="back-button"
- command="OrganizerCommand:Back"
- tooltiptext="&backButton.tooltip;"
- disabled="true"/>
-
- <toolbarbutton id="forward-button"
- command="OrganizerCommand:Forward"
- tooltiptext="&forwardButton.tooltip;"
- disabled="true"/>
-
-#ifdef XP_MACOSX
- <toolbarbutton type="menu" class="tabbable"
- onpopupshowing="document.getElementById('placeContent').focus()"
-#else
- <menubar id="placesMenu">
- <menu accesskey="&organize.accesskey;" class="menu-iconic"
-#endif
- id="organizeButton" label="&organize.label;"
- tooltiptext="&organize.tooltip;">
- <menupopup id="organizeButtonPopup">
- <menuitem id="newbookmark"
- command="placesCmd_new:bookmark"
- label="&cmd.new_bookmark.label;"
- accesskey="&cmd.new_bookmark.accesskey;"/>
- <menuitem id="newfolder"
- command="placesCmd_new:folder"
- label="&cmd.new_folder.label;"
- accesskey="&cmd.new_folder.accesskey;"/>
- <menuitem id="newseparator"
- command="placesCmd_new:separator"
- label="&cmd.new_separator.label;"
- accesskey="&cmd.new_separator.accesskey;"/>
-
-#ifndef XP_MACOSX
- <menuseparator id="orgUndoSeparator"/>
-
- <menuitem id="orgUndo"
- command="cmd_undo"
- label="&undoCmd.label;"
- key="key_undo"
- accesskey="&undoCmd.accesskey;"/>
- <menuitem id="orgRedo"
- command="cmd_redo"
- label="&redoCmd.label;"
- key="key_redo"
- accesskey="&redoCmd.accesskey;"/>
-
- <menuseparator id="orgCutSeparator"/>
-
- <menuitem id="orgCut"
- command="cmd_cut"
- label="&cutCmd.label;"
- key="key_cut"
- accesskey="&cutCmd.accesskey;"
- selection="separator|link|folder|mixed"/>
- <menuitem id="orgCopy"
- command="cmd_copy"
- label="&copyCmd.label;"
- key="key_copy"
- accesskey="&copyCmd.accesskey;"
- selection="separator|link|folder|mixed"/>
- <menuitem id="orgPaste"
- command="cmd_paste"
- label="&pasteCmd.label;"
- key="key_paste"
- accesskey="&pasteCmd.accesskey;"
- selection="mutable"/>
- <menuitem id="orgDelete"
- command="cmd_delete"
- label="&deleteCmd.label;"
- key="key_delete"
- accesskey="&deleteCmd.accesskey;"/>
-
- <menuseparator id="selectAllSeparator"/>
-
- <menuitem id="orgSelectAll"
- command="cmd_selectAll"
- label="&selectAllCmd.label;"
- key="key_selectAll"
- accesskey="&selectAllCmd.accesskey;"/>
-
-#endif
- <menuseparator id="orgMoveSeparator"/>
-
- <menuitem id="orgMoveBookmarks"
- command="placesCmd_moveBookmarks"
- label="&cmd.moveBookmarks.label;"
- accesskey="&cmd.moveBookmarks.accesskey;"/>
-#ifdef XP_MACOSX
- <menuitem id="orgDelete"
- command="cmd_delete"
- label="&deleteCmd.label;"
- key="key_delete"
- accesskey="&deleteCmd.accesskey;"/>
-#else
- <menuseparator id="orgCloseSeparator"/>
-
- <menuitem id="orgClose"
- key="placesKey_close"
- label="&file.close.label;"
- accesskey="&file.close.accesskey;"
- oncommand="close();"/>
-#endif
- </menupopup>
-#ifdef XP_MACOSX
- </toolbarbutton>
- <toolbarbutton type="menu" class="tabbable"
-#else
- </menu>
- <menu accesskey="&views.accesskey;" class="menu-iconic"
-#endif
- id="viewMenu" label="&views.label;"
- tooltiptext="&views.tooltip;">
- <menupopup id="viewMenuPopup">
-
- <menu id="viewColumns"
- label="&view.columns.label;" accesskey="&view.columns.accesskey;">
- <menupopup onpopupshowing="ViewMenu.fillWithColumns(event, null, null, 'checkbox', null);"
- oncommand="ViewMenu.showHideColumn(event.target); event.stopPropagation();"/>
- </menu>
-
- <menu id="viewSort" label="&view.sort.label;"
- accesskey="&view.sort.accesskey;">
- <menupopup onpopupshowing="ViewMenu.populateSortMenu(event);"
- oncommand="ViewMenu.setSortColumn(event.target.column, null);">
- <menuitem id="viewUnsorted" type="radio" name="columns"
- label="&view.unsorted.label;" accesskey="&view.unsorted.accesskey;"
- oncommand="ViewMenu.setSortColumn(null, null);"/>
- <menuseparator id="directionSeparator"/>
- <menuitem id="viewSortAscending" type="radio" name="direction"
- label="&view.sortAscending.label;" accesskey="&view.sortAscending.accesskey;"
- oncommand="ViewMenu.setSortColumn(null, 'ascending'); event.stopPropagation();"/>
- <menuitem id="viewSortDescending" type="radio" name="direction"
- label="&view.sortDescending.label;" accesskey="&view.sortDescending.accesskey;"
- oncommand="ViewMenu.setSortColumn(null, 'descending'); event.stopPropagation();"/>
- </menupopup>
- </menu>
- </menupopup>
-#ifdef XP_MACOSX
- </toolbarbutton>
- <toolbarbutton type="menu" class="tabbable"
-#else
- </menu>
- <menu accesskey="&maintenance.accesskey;" class="menu-iconic"
-#endif
- id="maintenanceButton" label="&maintenance.label;"
- tooltiptext="&maintenance.tooltip;">
- <menupopup id="maintenanceButtonPopup">
- <menuitem id="backupBookmarks"
- command="OrganizerCommand_backup"
- label="&cmd.backup.label;"
- accesskey="&cmd.backup.accesskey;"/>
- <menu id="fileRestoreMenu" label="&cmd.restore2.label;"
- accesskey="&cmd.restore2.accesskey;">
- <menupopup id="fileRestorePopup" onpopupshowing="PlacesOrganizer.populateRestoreMenu();">
- <menuitem id="restoreFromFile"
- command="OrganizerCommand_restoreFromFile"
- label="&cmd.restoreFromFile.label;"
- accesskey="&cmd.restoreFromFile.accesskey;"/>
- </menupopup>
- </menu>
- <menuseparator/>
- <menuitem id="fileImport"
- command="OrganizerCommand_import"
- label="&importBookmarksFromHTML.label;"
- accesskey="&importBookmarksFromHTML.accesskey;"/>
- <menuitem id="fileExport"
- command="OrganizerCommand_export"
- label="&exportBookmarksToHTML.label;"
- accesskey="&exportBookmarksToHTML.accesskey;"/>
- </menupopup>
-#ifdef XP_MACOSX
- </toolbarbutton>
-#else
- </menu>
- </menubar>
-#endif
-
- <spacer id="libraryToolbarSpacer" flex="1"/>
-
- <textbox id="searchFilter"
- clickSelectsAll="true"
- type="search"
- aria-controls="placeContent"
- oncommand="PlacesSearchBox.search(this.value);"
- collection="bookmarks">
- </textbox>
- </toolbar>
- </toolbox>
-
- <hbox flex="1" id="placesView">
- <tree id="placesList"
- class="plain placesTree"
- type="places"
- hidecolumnpicker="true" context="placesContext"
- onselect="PlacesOrganizer.onPlaceSelected(true);"
- onclick="PlacesOrganizer.onPlacesListClick(event);"
- onfocus="PlacesOrganizer.updateDetailsPane(event);"
- seltype="single"
- persist="width"
- width="200"
- minwidth="100"
- maxwidth="400">
- <treecols>
- <treecol anonid="title" flex="1" primary="true" hideheader="true"/>
- </treecols>
- <treechildren flex="1"/>
- </tree>
- <splitter collapse="none" persist="state"></splitter>
- <vbox id="contentView" flex="4">
- <toolbox id="searchModifiers" hidden="true">
- <toolbar id="organizerScopeBar" class="chromeclass-toolbar" align="center">
- <label id="scopeBarTitle" value="&search.in.label;"/>
- <toolbarbutton id="scopeBarAll" class="small-margin"
- type="radio" group="scopeBar"
- oncommand="PlacesQueryBuilder.onScopeSelected(this);"
- label="&search.scopeBookmarks.label;"
- accesskey="&search.scopeBookmarks.accesskey;"/>
- <toolbarbutton id="scopeBarHistory" class="small-margin"
- type="radio" group="scopeBar"
- oncommand="PlacesQueryBuilder.onScopeSelected(this);"
- label="&search.scopeHistory.label;"
- accesskey="&search.scopeHistory.accesskey;"/>
- <toolbarbutton id="scopeBarDownloads" class="small-margin"
- type="radio" group="scopeBar"
- oncommand="PlacesQueryBuilder.onScopeSelected(this);"
- label="&search.scopeDownloads.label;"
- accesskey="&search.scopeDownloads.accesskey;"/>
- <toolbarbutton id="scopeBarFolder" class="small-margin"
- type="radio" group="scopeBar"
- oncommand="PlacesQueryBuilder.onScopeSelected(this);"
- accesskey="&search.scopeFolder.accesskey;"
- emptytitle="&search.scopeFolder.label;" flex="1"/>
- <!-- The folder scope button should flex but not take up more room
- than its label needs. The only simple way to do that is to
- set a really big flex on the spacer, e.g., 2^31 - 1. -->
- <spacer flex="2147483647"/>
- <button id="saveSearch" class="small-margin"
- label="&saveSearch.label;" accesskey="&saveSearch.accesskey;"
- command="OrganizerCommand_search:save"/>
- </toolbar>
- </toolbox>
- <deck id="placesViewsDeck"
- selectedIndex="0"
- flex="1">
- <tree id="placeContent"
- class="plain placesTree"
- context="placesContext"
- hidecolumnpicker="true"
- flex="1"
- type="places"
- flatList="true"
- selectfirstnode="true"
- enableColumnDrag="true"
- onfocus="PlacesOrganizer.updateDetailsPane(event)"
- onselect="PlacesOrganizer.updateDetailsPane(event)"
- onkeypress="ContentTree.onKeyPress(event);"
- onopenflatcontainer="PlacesOrganizer.openFlatContainer(aContainer);">
- <treecols id="placeContentColumns" context="placesColumnsContext">
- <treecol label="&col.name.label;" id="placesContentTitle" anonid="title" flex="5" primary="true" ordinal="1"
- persist="width hidden ordinal sortActive sortDirection"/>
- <splitter class="tree-splitter"/>
- <treecol label="&col.tags.label;" id="placesContentTags" anonid="tags" flex="2"
- persist="width hidden ordinal sortActive sortDirection"/>
- <splitter class="tree-splitter"/>
- <treecol label="&col.url.label;" id="placesContentUrl" anonid="url" flex="5"
- persist="width hidden ordinal sortActive sortDirection"/>
- <splitter class="tree-splitter"/>
- <treecol label="&col.lastvisit.label;" id="placesContentDate" anonid="date" flex="1" hidden="true"
- persist="width hidden ordinal sortActive sortDirection"/>
- <splitter class="tree-splitter"/>
- <treecol label="&col.visitcount.label;" id="placesContentVisitCount" anonid="visitCount" flex="1" hidden="true"
- persist="width hidden ordinal sortActive sortDirection"/>
- <splitter class="tree-splitter"/>
- <treecol label="&col.keyword.label;" id="placesContentKeyword" anonid="keyword" flex="1" hidden="true"
- persist="width hidden ordinal sortActive sortDirection"/>
- <splitter class="tree-splitter"/>
- <treecol label="&col.description.label;" id="placesContentDescription" anonid="description" flex="1" hidden="true"
- persist="width hidden ordinal sortActive sortDirection"/>
- <splitter class="tree-splitter"/>
- <treecol label="&col.dateadded.label;" id="placesContentDateAdded" anonid="dateAdded" flex="1" hidden="true"
- persist="width hidden ordinal sortActive sortDirection"/>
- <splitter class="tree-splitter"/>
- <treecol label="&col.lastmodified.label;" id="placesContentLastModified" anonid="lastModified" flex="1" hidden="true"
- persist="width hidden ordinal sortActive sortDirection"/>
- <splitter class="tree-splitter"/>
- <treecol label="&col.parentfolder.label;" id="placesContentParentFolder" anonid="parentFolder" flex="1" hidden="true"
- persist="width hidden ordinal"/>
- <splitter class="tree-splitter"/>
- <treecol label="&col.parentfolderpath.label;" id="placesContentParentFolderPath" anonid="parentFolderPath" flex="1" hidden="true"
- persist="width hidden ordinal"/>
- </treecols>
- <treechildren flex="1" onclick="ContentTree.onClick(event);"/>
- </tree>
- </deck>
- <deck id="detailsDeck" style="height: 11em;">
- <vbox id="itemsCountBox" align="center">
- <spacer flex="3"/>
- <label id="itemsCountText"/>
- <spacer flex="1"/>
- <description id="selectItemDescription">
- &detailsPane.selectAnItemText.description;
- </description>
- <spacer flex="3"/>
- </vbox>
- <vbox id="infoBox" minimal="true">
- <vbox id="editBookmarkPanelContent" flex="1"/>
- <hbox id="infoBoxExpanderWrapper" align="center">
-
- <button type="image" id="infoBoxExpander"
- class="expander-down"
- oncommand="PlacesOrganizer.toggleAdditionalInfoFields();"
- observes="paneElementsBroadcaster"/>
-
- <label id="infoBoxExpanderLabel"
- lesslabel="&detailsPane.less.label;"
- lessaccesskey="&detailsPane.less.accesskey;"
- morelabel="&detailsPane.more.label;"
- moreaccesskey="&detailsPane.more.accesskey;"
- value="&detailsPane.more.label;"
- accesskey="&detailsPane.more.accesskey;"
- control="infoBoxExpander"/>
-
- </hbox>
- </vbox>
- </deck>
- </vbox>
- </hbox>
-</window>
diff --git a/components/places/content/placesOverlay.xul b/components/places/content/placesOverlay.xul
deleted file mode 100644
index 59115a5..0000000
--- a/components/places/content/placesOverlay.xul
+++ /dev/null
@@ -1,247 +0,0 @@
-<!-- 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/. -->
-
-<!DOCTYPE overlay [
-<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
-%placesDTD;
-<!ENTITY % editMenuOverlayDTD SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
-%editMenuOverlayDTD;
-]>
-
-<overlay id="placesOverlay"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <script type="application/javascript"
- src="chrome://global/content/globalOverlay.js"/>
- <script type="application/javascript"
- src="chrome://browser/content/utilityOverlay.js"/>
- <script type="application/javascript"><![CDATA[
- // TODO: Bug 406371.
- // A bunch of browser code depends on us defining these, sad but true :(
- var Cc = Components.classes;
- var Ci = Components.interfaces;
- var Cr = Components.results;
-
- Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
- Components.utils.import("resource:///modules/PlacesUIUtils.jsm");
- ]]></script>
- <script type="application/javascript"
- src="chrome://browser/content/places/controller.js"/>
- <script type="application/javascript"
- src="chrome://browser/content/places/treeView.js"/>
-
- <!-- Bookmarks and history tooltip -->
- <tooltip id="bhTooltip" noautohide="true"
- onpopupshowing="return window.top.BookmarksEventHandler.fillInBHTooltip(document, event)">
- <vbox id="bhTooltipTextBox" flex="1">
- <label id="bhtTitleText" class="tooltip-label" />
- <label id="bhtUrlText" crop="center" class="tooltip-label" />
- </vbox>
- </tooltip>
-
- <commandset id="placesCommands"
- commandupdater="true"
- events="focus,sort,places"
- oncommandupdate="goUpdatePlacesCommands();">
- <command id="placesCmd_open"
- oncommand="goDoPlacesCommand('placesCmd_open');"/>
- <command id="placesCmd_open:window"
- oncommand="goDoPlacesCommand('placesCmd_open:window');"/>
- <command id="placesCmd_open:privatewindow"
- oncommand="goDoPlacesCommand('placesCmd_open:privatewindow');"/>
- <command id="placesCmd_open:tab"
- oncommand="goDoPlacesCommand('placesCmd_open:tab');"/>
-
- <command id="placesCmd_new:bookmark"
- oncommand="goDoPlacesCommand('placesCmd_new:bookmark');"/>
- <command id="placesCmd_new:livemark"
- oncommand="goDoPlacesCommand('placesCmd_new:livemark');"/>
- <command id="placesCmd_new:folder"
- oncommand="goDoPlacesCommand('placesCmd_new:folder');"/>
- <command id="placesCmd_new:separator"
- oncommand="goDoPlacesCommand('placesCmd_new:separator');"/>
- <command id="placesCmd_show:info"
- oncommand="goDoPlacesCommand('placesCmd_show:info');"/>
- <command id="placesCmd_rename"
- oncommand="goDoPlacesCommand('placesCmd_show:info');"
- observes="placesCmd_show:info"/>
- <command id="placesCmd_reload"
- oncommand="goDoPlacesCommand('placesCmd_reload');"/>
- <command id="placesCmd_sortBy:name"
- oncommand="goDoPlacesCommand('placesCmd_sortBy:name');"/>
- <command id="placesCmd_moveBookmarks"
- oncommand="goDoPlacesCommand('placesCmd_moveBookmarks');"/>
- <command id="placesCmd_deleteDataHost"
- oncommand="goDoPlacesCommand('placesCmd_deleteDataHost');"/>
- <command id="placesCmd_createBookmark"
- oncommand="goDoPlacesCommand('placesCmd_createBookmark');"/>
- <command id="placesCmd_openParentFolder"
- oncommand="goDoPlacesCommand('placesCmd_openParentFolder');"/>
-
- <!-- Special versions of cut/copy/paste/delete which check for an open context menu. -->
- <command id="placesCmd_cut"
- oncommand="goDoPlacesCommand('placesCmd_cut');"/>
- <command id="placesCmd_copy"
- oncommand="goDoPlacesCommand('placesCmd_copy');"/>
- <command id="placesCmd_paste"
- oncommand="goDoPlacesCommand('placesCmd_paste');"/>
- <command id="placesCmd_delete"
- oncommand="goDoPlacesCommand('placesCmd_delete');"/>
- </commandset>
-
- <keyset id="placesCommandKeys">
- <key id="key_placesCmd_openParentFolder"
- keycode="VK_F1"
- command="placesCmd_openParentFolder"
- modifiers="accel,shift"/>
- </keyset>
-
- <menupopup id="placesContext"
- onpopupshowing="this._view = PlacesUIUtils.getViewForNode(document.popupNode);
- return this._view.buildContextMenu(this);"
- onpopuphiding="this._view.destroyContextMenu();">
- <menuitem id="placesContext_open"
- command="placesCmd_open"
- label="&cmd.open.label;"
- accesskey="&cmd.open.accesskey;"
- default="true"
- selectiontype="single"
- selection="link"/>
- <menuitem id="placesContext_open:newtab"
- command="placesCmd_open:tab"
- label="&cmd.open_tab.label;"
- accesskey="&cmd.open_tab.accesskey;"
- selectiontype="single"
- selection="link"/>
- <menuitem id="placesContext_openContainer:tabs"
- oncommand="var view = PlacesUIUtils.getViewForNode(document.popupNode);
- view.controller.openSelectionInTabs(event);"
- onclick="checkForMiddleClick(this, event);"
- label="&cmd.open_all_in_tabs.label;"
- accesskey="&cmd.open_all_in_tabs.accesskey;"
- selectiontype="single"
- selection="folder|host|query"/>
- <menuitem id="placesContext_openLinks:tabs"
- oncommand="var view = PlacesUIUtils.getViewForNode(document.popupNode);
- view.controller.openSelectionInTabs(event);"
- onclick="checkForMiddleClick(this, event);"
- label="&cmd.open_all_in_tabs.label;"
- accesskey="&cmd.open_all_in_tabs.accesskey;"
- selectiontype="multiple"
- selection="link"/>
- <menuitem id="placesContext_open:newwindow"
- command="placesCmd_open:window"
- label="&cmd.open_window.label;"
- accesskey="&cmd.open_window.accesskey;"
- selectiontype="single"
- selection="link"/>
- <menuitem id="placesContext_open:newprivatewindow"
- command="placesCmd_open:privatewindow"
- label="&cmd.open_private_window.label;"
- accesskey="&cmd.open_private_window.accesskey;"
- selectiontype="single"
- selection="link"
- hideifprivatebrowsing="true"/>
- <menuseparator id="placesContext_openSeparator"/>
- <menuitem id="placesContext_new:bookmark"
- command="placesCmd_new:bookmark"
- label="&cmd.new_bookmark.label;"
- accesskey="&cmd.new_bookmark.accesskey;"
- selectiontype="any"
- hideifnoinsertionpoint="true"/>
- <menuitem id="placesContext_new:folder"
- command="placesCmd_new:folder"
- label="&cmd.new_folder.label;"
- accesskey="&cmd.context_new_folder.accesskey;"
- selectiontype="any"
- hideifnoinsertionpoint="true"/>
- <menuitem id="placesContext_new:separator"
- command="placesCmd_new:separator"
- label="&cmd.new_separator.label;"
- accesskey="&cmd.new_separator.accesskey;"
- closemenu="single"
- selectiontype="any"
- hideifnoinsertionpoint="true"/>
- <menuseparator id="placesContext_newSeparator"/>
- <menuitem id="placesContext_createBookmark"
- command="placesCmd_createBookmark"
- label="&cmd.bookmarkLink.label;"
- accesskey="&cmd.bookmarkLink.accesskey;"
- selection="link"
- forcehideselection="bookmark|tagChild"/>
- <menuitem id="placesContext_cut"
- command="placesCmd_cut"
- label="&cutCmd.label;"
- accesskey="&cutCmd.accesskey;"
- closemenu="single"
- selection="bookmark|folder|separator|query"
- forcehideselection="tagChild|livemarkChild"/>
- <menuitem id="placesContext_copy"
- command="placesCmd_copy"
- label="&copyCmd.label;"
- closemenu="single"
- accesskey="&copyCmd.accesskey;"/>
- <menuitem id="placesContext_paste"
- command="placesCmd_paste"
- label="&pasteCmd.label;"
- closemenu="single"
- accesskey="&pasteCmd.accesskey;"
- selectiontype="any"
- hideifnoinsertionpoint="true"/>
- <menuseparator id="placesContext_editSeparator"/>
- <menuitem id="placesContext_delete"
- command="placesCmd_delete"
- label="&deleteCmd.label;"
- accesskey="&deleteCmd.accesskey;"
- closemenu="single"
- selection="bookmark|tagChild|folder|query|dynamiccontainer|separator|host"/>
- <menuitem id="placesContext_delete_history"
- command="placesCmd_delete"
- label="&cmd.delete.label;"
- accesskey="&cmd.delete.accesskey;"
- closemenu="single"
- selection="link"
- forcehideselection="bookmark|livemarkChild"/>
- <menuitem id="placesContext_deleteHost"
- command="placesCmd_deleteDataHost"
- label="&cmd.deleteDomainData.label;"
- accesskey="&cmd.deleteDomainData.accesskey;"
- closemenu="single"
- selection="link|host"
- selectiontype="single"
- hideifprivatebrowsing="true"
- forcehideselection="bookmark|livemarkChild"/>
- <menuseparator id="placesContext_deleteSeparator"/>
- <menuitem id="placesContext_reload"
- command="placesCmd_reload"
- label="&cmd.reloadLivebookmark.label;"
- accesskey="&cmd.reloadLivebookmark.accesskey;"
- closemenu="single"
- selection="livemark/feedURI"/>
- <menuitem id="placesContext_sortBy:name"
- command="placesCmd_sortBy:name"
- label="&cmd.sortby_name.label;"
- accesskey="&cmd.context_sortby_name.accesskey;"
- closemenu="single"
- selection="folder"/>
- <menuseparator id="placesContext_sortSeparator"/>
- <menuitem id="placesContext_openParentFolder"
- command="placesCmd_openParentFolder"
- label="&cmd.openParentFolder.label;"
- key="key_placesCmd_openParentFolder"
- accesskey="&cmd.openParentFolder.accesskey;"
- selectiontype="single"
- selection="bookmark"
- forcehideselection="livemarkChild|livemark/feedURI|PlacesOrganizer/OrganizerQuery"/>
- <menuseparator id="placesContext_parentFolderSeparator"/>
- <menuitem id="placesContext_show:info"
- command="placesCmd_show:info"
- label="&cmd.properties.label;"
- accesskey="&cmd.properties.accesskey;"
- selection="bookmark|folder|query"
- forcehideselection="livemarkChild"/>
- </menupopup>
-
-</overlay>
diff --git a/components/places/content/sidebarUtils.js b/components/places/content/sidebarUtils.js
deleted file mode 100644
index 06ed537..0000000
--- a/components/places/content/sidebarUtils.js
+++ /dev/null
@@ -1,108 +0,0 @@
-# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
-# 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/.
-
-var SidebarUtils = {
- handleTreeClick: function SU_handleTreeClick(aTree, aEvent, aGutterSelect) {
- // right-clicks are not handled here
- if (aEvent.button == 2)
- return;
-
- var tbo = aTree.treeBoxObject;
- var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);
-
- if (cell.row == -1 || cell.childElt == "twisty")
- return;
-
- var mouseInGutter = false;
- if (aGutterSelect) {
- var rect = tbo.getCoordsForCellItem(cell.row, cell.col, "image");
- // getCoordsForCellItem returns the x coordinate in logical coordinates
- // (i.e., starting from the left and right sides in LTR and RTL modes,
- // respectively.) Therefore, we make sure to exclude the blank area
- // before the tree item icon (that is, to the left or right of it in
- // LTR and RTL modes, respectively) from the click target area.
- var isRTL = window.getComputedStyle(aTree, null).direction == "rtl";
- if (isRTL)
- mouseInGutter = aEvent.clientX > rect.x;
- else
- mouseInGutter = aEvent.clientX < rect.x;
- }
-
-#ifdef XP_MACOSX
- var modifKey = aEvent.metaKey || aEvent.shiftKey;
-#else
- var modifKey = aEvent.ctrlKey || aEvent.shiftKey;
-#endif
-
- var isContainer = tbo.view.isContainer(cell.row);
- var openInTabs = isContainer &&
- (aEvent.button == 1 ||
- (aEvent.button == 0 && modifKey)) &&
- PlacesUtils.hasChildURIs(tbo.view.nodeForTreeIndex(cell.row), true);
-
- if (aEvent.button == 0 && isContainer && !openInTabs) {
- tbo.view.toggleOpenState(cell.row);
- return;
- }
- else if (!mouseInGutter && openInTabs &&
- aEvent.originalTarget.localName == "treechildren") {
- tbo.view.selection.select(cell.row);
- PlacesUIUtils.openContainerNodeInTabs(aTree.selectedNode, aEvent, aTree);
- }
- else if (!mouseInGutter && !isContainer &&
- aEvent.originalTarget.localName == "treechildren") {
- // Clear all other selection since we're loading a link now. We must
- // do this *before* attempting to load the link since openURL uses
- // selection as an indication of which link to load.
- tbo.view.selection.select(cell.row);
- PlacesUIUtils.openNodeWithEvent(aTree.selectedNode, aEvent, aTree);
- }
- },
-
- handleTreeKeyPress: function SU_handleTreeKeyPress(aEvent) {
- // XXX Bug 627901: Post Fx4, this method should take a tree parameter.
- let tree = aEvent.target;
- let node = tree.selectedNode;
- if (node) {
- if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
- PlacesUIUtils.openNodeWithEvent(node, aEvent, tree);
- }
- },
-
- /**
- * The following function displays the URL of a node that is being
- * hovered over.
- */
- handleTreeMouseMove: function SU_handleTreeMouseMove(aEvent) {
- if (aEvent.target.localName != "treechildren")
- return;
-
- var tree = aEvent.target.parentNode;
- var tbo = tree.treeBoxObject;
- var cell = tbo.getCellAt(aEvent.clientX, aEvent.clientY);
-
- // cell.row is -1 when the mouse is hovering an empty area within the tree.
- // To avoid showing a URL from a previously hovered node for a currently
- // hovered non-url node, we must clear the moused-over URL in these cases.
- if (cell.row != -1) {
- var node = tree.view.nodeForTreeIndex(cell.row);
- if (PlacesUtils.nodeIsURI(node))
- this.setMouseoverURL(node.uri);
- else
- this.setMouseoverURL("");
- }
- else
- this.setMouseoverURL("");
- },
-
- setMouseoverURL: function SU_setMouseoverURL(aURL) {
- // When the browser window is closed with an open sidebar, the sidebar
- // unload event happens after the browser's one. In this case
- // top.XULBrowserWindow has been nullified already.
- if (top.XULBrowserWindow) {
- top.XULBrowserWindow.setOverLink(aURL, null);
- }
- }
-};
diff --git a/components/places/content/tree.xml b/components/places/content/tree.xml
deleted file mode 100644
index 05b0169..0000000
--- a/components/places/content/tree.xml
+++ /dev/null
@@ -1,789 +0,0 @@
-<?xml version="1.0"?>
-
-<!-- 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/. -->
-
-<bindings id="placesTreeBindings"
- xmlns="http://www.mozilla.org/xbl"
- xmlns:xbl="http://www.mozilla.org/xbl"
- xmlns:html="http://www.w3.org/1999/xhtml"
- xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
- <binding id="places-tree" extends="chrome://global/content/bindings/tree.xml#tree">
- <implementation>
- <constructor><![CDATA[
- // Force an initial build.
- if (this.place)
- this.place = this.place;
- ]]></constructor>
-
- <destructor><![CDATA[
- // Break the treeviewer->result->treeviewer cycle.
- // Note: unsetting the result's viewer also unsets
- // the viewer's reference to our treeBoxObject.
- var result = this.result;
- if (result) {
- result.root.containerOpen = false;
- }
-
- // Unregister the controllber before unlinking the view, otherwise it
- // may still try to update commands on a view with a null result.
- if (this._controller) {
- this._controller.terminate();
- this.controllers.removeController(this._controller);
- }
-
- this.view = null;
- ]]></destructor>
-
- <property name="controller"
- readonly="true"
- onget="return this._controller"/>
-
- <!-- overriding -->
- <property name="view">
- <getter><![CDATA[
- try {
- return this.treeBoxObject.view.wrappedJSObject;
- }
- catch(e) {
- return null;
- }
- ]]></getter>
- <setter><![CDATA[
- return this.treeBoxObject.view = val;
- ]]></setter>
- </property>
-
- <property name="associatedElement"
- readonly="true"
- onget="return this"/>
-
- <method name="applyFilter">
- <parameter name="filterString"/>
- <parameter name="folderRestrict"/>
- <parameter name="includeHidden"/>
- <body><![CDATA[
- // preserve grouping
- var queryNode = PlacesUtils.asQuery(this.result.root);
- var options = queryNode.queryOptions.clone();
-
- // Make sure we're getting uri results.
- // We do not yet support searching into grouped queries or into
- // tag containers, so we must fall to the default case.
- if (PlacesUtils.nodeIsHistoryContainer(queryNode) ||
- options.resultType == options.RESULTS_AS_TAG_QUERY ||
- options.resultType == options.RESULTS_AS_TAG_CONTENTS)
- options.resultType = options.RESULTS_AS_URI;
-
- var query = PlacesUtils.history.getNewQuery();
- query.searchTerms = filterString;
-
- if (folderRestrict) {
- query.setFolders(folderRestrict, folderRestrict.length);
- options.queryType = options.QUERY_TYPE_BOOKMARKS;
- }
-
- options.includeHidden = !!includeHidden;
-
- this.load([query], options);
- ]]></body>
- </method>
-
- <method name="load">
- <parameter name="queries"/>
- <parameter name="options"/>
- <body><![CDATA[
- let result = PlacesUtils.history
- .executeQueries(queries, queries.length,
- options);
- let callback;
- if (this.flatList) {
- let onOpenFlatContainer = this.onOpenFlatContainer;
- if (onOpenFlatContainer)
- callback = new Function("aContainer", onOpenFlatContainer);
- }
-
- if (!this._controller) {
- this._controller = new PlacesController(this);
- this.controllers.appendController(this._controller);
- }
-
- let treeView = new PlacesTreeView(this.flatList, callback, this._controller);
-
- // Observer removal is done within the view itself. When the tree
- // goes away, treeboxobject calls view.setTree(null), which then
- // calls removeObserver.
- result.addObserver(treeView, false);
- this.view = treeView;
-
- if (this.getAttribute("selectfirstnode") == "true" && treeView.rowCount > 0) {
- treeView.selection.select(0);
- }
-
- this._cachedInsertionPoint = undefined;
- ]]></body>
- </method>
-
- <property name="flatList">
- <getter><![CDATA[
- return this.getAttribute("flatList") == "true";
- ]]></getter>
- <setter><![CDATA[
- if (this.flatList != val) {
- this.setAttribute("flatList", val);
- // reload with the last place set
- if (this.place)
- this.place = this.place;
- }
- return val;
- ]]></setter>
- </property>
-
- <property name="onOpenFlatContainer">
- <getter><![CDATA[
- return this.getAttribute("onopenflatcontainer");
- ]]></getter>
- <setter><![CDATA[
- if (this.onOpenFlatContainer != val) {
- this.setAttribute("onopenflatcontainer", val);
- // reload with the last place set
- if (this.place)
- this.place = this.place;
- }
- return val;
- ]]></setter>
- </property>
-
- <!--
- Causes a particular node represented by the specified placeURI to be
- selected in the tree. All containers above the node in the hierarchy
- will be opened, so that the node is visible.
- -->
- <method name="selectPlaceURI">
- <parameter name="placeURI"/>
- <body><![CDATA[
- // Do nothing if a node matching the given uri is already selected
- if (this.hasSelection && this.selectedNode.uri == placeURI)
- return;
-
- function findNode(container, placeURI, nodesURIChecked) {
- var containerURI = container.uri;
- if (containerURI == placeURI)
- return container;
- if (nodesURIChecked.indexOf(containerURI) != -1)
- return null;
-
- // never check the contents of the same query
- nodesURIChecked.push(containerURI);
-
- var wasOpen = container.containerOpen;
- if (!wasOpen)
- container.containerOpen = true;
- for (var i = 0; i < container.childCount; ++i) {
- var child = container.getChild(i);
- var childURI = child.uri;
- if (childURI == placeURI)
- return child;
- else if (PlacesUtils.nodeIsContainer(child)) {
- var nested = findNode(PlacesUtils.asContainer(child), placeURI, nodesURIChecked);
- if (nested)
- return nested;
- }
- }
-
- if (!wasOpen)
- container.containerOpen = false;
-
- return null;
- }
-
- var container = this.result.root;
- NS_ASSERT(container, "No result, cannot select place URI!");
- if (!container)
- return;
-
- var child = findNode(container, placeURI, []);
- if (child)
- this.selectNode(child);
- else {
- // If the specified child could not be located, clear the selection
- var selection = this.view.selection;
- selection.clearSelection();
- }
- ]]></body>
- </method>
-
- <!--
- Causes a particular node to be selected in the tree, resulting in all
- containers above the node in the hierarchy to be opened, so that the
- node is visible.
- -->
- <method name="selectNode">
- <parameter name="node"/>
- <body><![CDATA[
- var view = this.view;
-
- var parent = node.parent;
- if (parent && !parent.containerOpen) {
- // Build a list of all of the nodes that are the parent of this one
- // in the result.
- var parents = [];
- var root = this.result.root;
- while (parent && parent != root) {
- parents.push(parent);
- parent = parent.parent;
- }
-
- // Walk the list backwards (opening from the root of the hierarchy)
- // opening each folder as we go.
- for (var i = parents.length - 1; i >= 0; --i) {
- var index = view.treeIndexForNode(parents[i]);
- if (index != Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE &&
- view.isContainer(index) && !view.isContainerOpen(index))
- view.toggleOpenState(index);
- }
- // Select the specified node...
- }
-
- var index = view.treeIndexForNode(node);
- if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE)
- return;
-
- view.selection.select(index);
- // ... and ensure it's visible, not scrolled off somewhere.
- this.treeBoxObject.ensureRowIsVisible(index);
- ]]></body>
- </method>
-
- <!-- nsIPlacesView -->
- <property name="result">
- <getter><![CDATA[
- try {
- return this.view.QueryInterface(Ci.nsINavHistoryResultObserver).result;
- }
- catch (e) {
- return null;
- }
- ]]></getter>
- </property>
-
- <!-- nsIPlacesView -->
- <property name="place">
- <getter><![CDATA[
- return this.getAttribute("place");
- ]]></getter>
- <setter><![CDATA[
- this.setAttribute("place", val);
-
- var queriesRef = { };
- var queryCountRef = { };
- var optionsRef = { };
- PlacesUtils.history.queryStringToQueries(val, queriesRef, queryCountRef, optionsRef);
- if (queryCountRef.value == 0)
- queriesRef.value = [PlacesUtils.history.getNewQuery()];
- if (!optionsRef.value)
- optionsRef.value = PlacesUtils.history.getNewQueryOptions();
-
- this.load(queriesRef.value, optionsRef.value);
-
- return val;
- ]]></setter>
- </property>
-
- <!-- nsIPlacesView -->
- <property name="hasSelection">
- <getter><![CDATA[
- return this.view && this.view.selection.count >= 1;
- ]]></getter>
- </property>
-
- <!-- nsIPlacesView -->
- <property name="selectedNodes">
- <getter><![CDATA[
- let nodes = [];
- if (!this.hasSelection)
- return nodes;
-
- let selection = this.view.selection;
- let rc = selection.getRangeCount();
- let resultview = this.view;
- for (let i = 0; i < rc; ++i) {
- let min = { }, max = { };
- selection.getRangeAt(i, min, max);
-
- for (let j = min.value; j <= max.value; ++j)
- nodes.push(resultview.nodeForTreeIndex(j));
- }
- return nodes;
- ]]></getter>
- </property>
-
- <method name="toggleCutNode">
- <parameter name="aNode"/>
- <parameter name="aValue"/>
- <body><![CDATA[
- this.view.toggleCutNode(aNode, aValue);
- ]]></body>
- </method>
-
- <!-- nsIPlacesView -->
- <property name="removableSelectionRanges">
- <getter><![CDATA[
- // This property exists in addition to selectedNodes because it
- // encodes selection ranges (which only occur in list views) into
- // the return value. For each removed range, the index at which items
- // will be re-inserted upon the remove transaction being performed is
- // the first index of the range, so that the view updates correctly.
- //
- // For example, if we remove rows 2,3,4 and 7,8 from a list, when we
- // undo that operation, if we insert what was at row 3 at row 3 again,
- // it will show up _after_ the item that was at row 5. So we need to
- // insert all items at row 2, and the tree view will update correctly.
- //
- // Also, this function collapses the selection to remove redundant
- // data, e.g. when deleting this selection:
- //
- // http://www.foo.com/
- // (-) Some Folder
- // http://www.bar.com/
- //
- // ... returning http://www.bar.com/ as part of the selection is
- // redundant because it is implied by removing "Some Folder". We
- // filter out all such redundancies since some partial amount of
- // the folder's children may be selected.
- //
- let nodes = [];
- if (!this.hasSelection)
- return nodes;
-
- var selection = this.view.selection;
- var rc = selection.getRangeCount();
- var resultview = this.view;
- // This list is kept independently of the range selected (i.e. OUTSIDE
- // the for loop) since the row index of a container is unique for the
- // entire view, and we could have some really wacky selection and we
- // don't want to blow up.
- var containers = { };
- for (var i = 0; i < rc; ++i) {
- var range = [];
- var min = { }, max = { };
- selection.getRangeAt(i, min, max);
-
- for (var j = min.value; j <= max.value; ++j) {
- if (this.view.isContainer(j))
- containers[j] = true;
- if (!(this.view.getParentIndex(j) in containers))
- range.push(resultview.nodeForTreeIndex(j));
- }
- nodes.push(range);
- }
- return nodes;
- ]]></getter>
- </property>
-
- <!-- nsIPlacesView -->
- <property name="draggableSelection"
- onget="return this.selectedNodes"/>
-
- <!-- nsIPlacesView -->
- <property name="selectedNode">
- <getter><![CDATA[
- var view = this.view;
- if (!view || view.selection.count != 1)
- return null;
-
- var selection = view.selection;
- var min = { }, max = { };
- selection.getRangeAt(0, min, max);
-
- return this.view.nodeForTreeIndex(min.value);
- ]]></getter>
- </property>
-
- <!-- nsIPlacesView -->
- <property name="insertionPoint">
- <getter><![CDATA[
- // invalidated on selection and focus changes
- if (this._cachedInsertionPoint !== undefined)
- return this._cachedInsertionPoint;
-
- // there is no insertion point for history queries
- // so bail out now and save a lot of work when updating commands
- var resultNode = this.result.root;
- if (PlacesUtils.nodeIsQuery(resultNode) &&
- PlacesUtils.asQuery(resultNode).queryOptions.queryType ==
- Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
- return this._cachedInsertionPoint = null;
-
- var orientation = Ci.nsITreeView.DROP_BEFORE;
- // If there is no selection, insert at the end of the container.
- if (!this.hasSelection) {
- var index = this.view.rowCount - 1;
- this._cachedInsertionPoint =
- this._getInsertionPoint(index, orientation);
- return this._cachedInsertionPoint;
- }
-
- // This is a two-part process. The first part is determining the drop
- // orientation.
- // * The default orientation is to drop _before_ the selected item.
- // * If the selected item is a container, the default orientation
- // is to drop _into_ that container.
- //
- // Warning: It may be tempting to use tree indexes in this code, but
- // you must not, since the tree is nested and as your tree
- // index may change when folders before you are opened and
- // closed. You must convert your tree index to a node, and
- // then use getChildIndex to find your absolute index in
- // the parent container instead.
- //
- var resultView = this.view;
- var selection = resultView.selection;
- var rc = selection.getRangeCount();
- var min = { }, max = { };
- selection.getRangeAt(rc - 1, min, max);
-
- // If the sole selection is a container, and we are not in
- // a flatlist, insert into it.
- // Note that this only applies to _single_ selections,
- // if the last element within a multi-selection is a
- // container, insert _adjacent_ to the selection.
- //
- // If the sole selection is the bookmarks toolbar folder, we insert
- // into it even if it is not opened
- var itemId =
- PlacesUtils.getConcreteItemId(resultView.nodeForTreeIndex(max.value));
- if (selection.count == 1 && resultView.isContainer(max.value) &&
- !this.flatList)
- orientation = Ci.nsITreeView.DROP_ON;
-
- this._cachedInsertionPoint =
- this._getInsertionPoint(max.value, orientation);
- return this._cachedInsertionPoint;
- ]]></getter>
- </property>
-
- <method name="_getInsertionPoint">
- <parameter name="index"/>
- <parameter name="orientation"/>
- <body><![CDATA[
- var result = this.result;
- var resultview = this.view;
- var container = result.root;
- var dropNearItemId = -1;
- NS_ASSERT(container, "null container");
- // When there's no selection, assume the container is the container
- // the view is populated from (i.e. the result's itemId).
- if (index != -1) {
- var lastSelected = resultview.nodeForTreeIndex(index);
- if (resultview.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
- // If the last selected item is an open container, append _into_
- // it, rather than insert adjacent to it.
- container = lastSelected;
- index = -1;
- }
- else if (lastSelected.containerOpen &&
- orientation == Ci.nsITreeView.DROP_AFTER &&
- lastSelected.hasChildren) {
- // If the last selected item is an open container and the user is
- // trying to drag into it as a first item, really insert into it.
- container = lastSelected;
- orientation = Ci.nsITreeView.DROP_ON;
- index = 0;
- }
- else {
- // Use the last-selected node's container.
- container = lastSelected.parent;
-
- // See comment in the treeView.js's copy of this method
- if (!container || !container.containerOpen)
- return null;
-
- // Avoid the potentially expensive call to getChildIndex
- // if we know this container doesn't allow insertion
- if (PlacesControllerDragHelper.disallowInsertion(container))
- return null;
-
- var queryOptions = PlacesUtils.asQuery(result.root).queryOptions;
- if (queryOptions.sortingMode !=
- Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
- // If we are within a sorted view, insert at the end
- index = -1;
- }
- else if (queryOptions.excludeItems ||
- queryOptions.excludeQueries ||
- queryOptions.excludeReadOnlyFolders) {
- // Some item may be invisible, insert near last selected one.
- // We don't replace index here to avoid requests to the db,
- // instead it will be calculated later by the controller.
- index = -1;
- dropNearItemId = lastSelected.itemId;
- }
- else {
- var lsi = container.getChildIndex(lastSelected);
- index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
- }
- }
- }
-
- if (PlacesControllerDragHelper.disallowInsertion(container))
- return null;
-
- return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
- index, orientation,
- PlacesUtils.nodeIsTagQuery(container),
- dropNearItemId);
- ]]></body>
- </method>
-
- <!-- nsIPlacesView -->
- <method name="selectAll">
- <body><![CDATA[
- this.view.selection.selectAll();
- ]]></body>
- </method>
-
- <!-- This method will select the first node in the tree that matches
- each given item id. It will open any parent nodes that it needs
- to in order to show the selected items.
- -->
- <method name="selectItems">
- <parameter name="aIDs"/>
- <parameter name="aOpenContainers"/>
- <body><![CDATA[
- // By default, we do search and select within containers which were
- // closed (note that containers in which nodes were not found are
- // closed).
- if (aOpenContainers === undefined)
- aOpenContainers = true;
-
- var ids = aIDs; // don't manipulate the caller's array
-
- // Array of nodes found by findNodes which are to be selected
- var nodes = [];
-
- // Array of nodes found by findNodes which should be opened
- var nodesToOpen = [];
-
- // A set of URIs of container-nodes that were previously searched,
- // and thus shouldn't be searched again. This is empty at the initial
- // start of the recursion and gets filled in as the recursion
- // progresses.
- var nodesURIChecked = [];
-
- /**
- * Recursively search through a node's children for items
- * with the given IDs. When a matching item is found, remove its ID
- * from the IDs array, and add the found node to the nodes dictionary.
- *
- * NOTE: This method will leave open any node that had matching items
- * in its subtree.
- */
- function findNodes(node) {
- var foundOne = false;
- // See if node matches an ID we wanted; add to results.
- // For simple folder queries, check both itemId and the concrete
- // item id.
- var index = ids.indexOf(node.itemId);
- if (index == -1 &&
- node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT)
- index = ids.indexOf(PlacesUtils.asQuery(node).folderItemId);
-
- if (index != -1) {
- nodes.push(node);
- foundOne = true;
- ids.splice(index, 1);
- }
-
- if (ids.length == 0 || !PlacesUtils.nodeIsContainer(node) ||
- nodesURIChecked.indexOf(node.uri) != -1)
- return foundOne;
-
- PlacesUtils.asContainer(node);
- if (!aOpenContainers && !node.containerOpen)
- return foundOne;
-
- nodesURIChecked.push(node.uri);
-
- // Remember the beginning state so that we can re-close
- // this node if we don't find any additional results here.
- var previousOpenness = node.containerOpen;
- node.containerOpen = true;
- for (var child = 0; child < node.childCount && ids.length > 0;
- child++) {
- var childNode = node.getChild(child);
- var found = findNodes(childNode);
- if (!foundOne)
- foundOne = found;
- }
-
- // If we didn't find any additional matches in this node's
- // subtree, revert the node to its previous openness.
- if (foundOne)
- nodesToOpen.unshift(node);
- node.containerOpen = previousOpenness;
- return foundOne;
- }
-
- // Disable notifications while looking for nodes.
- let result = this.result;
- let didSuppressNotifications = result.suppressNotifications;
- if (!didSuppressNotifications)
- result.suppressNotifications = true
- try {
- findNodes(this.result.root);
- }
- finally {
- if (!didSuppressNotifications)
- result.suppressNotifications = false;
- }
-
- // For all the nodes we've found, highlight the corresponding
- // index in the tree.
- var resultview = this.view;
- var selection = this.view.selection;
- selection.selectEventsSuppressed = true;
- selection.clearSelection();
- // Open nodes containing found items
- for (var i = 0; i < nodesToOpen.length; i++) {
- nodesToOpen[i].containerOpen = true;
- }
- for (var i = 0; i < nodes.length; i++) {
- var index = resultview.treeIndexForNode(nodes[i]);
- if (index == Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE)
- continue;
- selection.rangedSelect(index, index, true);
- }
- selection.selectEventsSuppressed = false;
- ]]></body>
- </method>
-
- <field name="_contextMenuShown">false</field>
-
- <method name="buildContextMenu">
- <parameter name="aPopup"/>
- <body><![CDATA[
- this._contextMenuShown = true;
- return this.controller.buildContextMenu(aPopup);
- ]]></body>
- </method>
-
- <method name="destroyContextMenu">
- <parameter name="aPopup"/>
- this._contextMenuShown = false;
- <body/>
- </method>
-
- <property name="ownerWindow"
- readonly="true"
- onget="return window;"/>
-
- <field name="_active">true</field>
- <property name="active"
- onget="return this._active"
- onset="return this._active = val"/>
-
- </implementation>
- <handlers>
- <handler event="focus"><![CDATA[
- this._cachedInsertionPoint = undefined;
-
- // See select handler. We need the sidebar's places commandset to be
- // updated as well
- document.commandDispatcher.updateCommands("focus");
- ]]></handler>
- <handler event="select"><![CDATA[
- this._cachedInsertionPoint = undefined;
-
- // This additional complexity is here for the sidebars
- var win = window;
- while (true) {
- win.document.commandDispatcher.updateCommands("focus");
- if (win == window.top)
- break;
-
- win = win.parent;
- }
- ]]></handler>
-
- <handler event="dragstart"><![CDATA[
- if (event.target.localName != "treechildren")
- return;
-
- let nodes = this.selectedNodes;
- for (let i = 0; i < nodes.length; i++) {
- let node = nodes[i];
-
- // Disallow dragging the root node of a tree.
- if (!node.parent) {
- event.preventDefault();
- event.stopPropagation();
- return;
- }
-
- // If this node is child of a readonly container (e.g. a livemark)
- // or cannot be moved, we must force a copy.
- if (!PlacesControllerDragHelper.canMoveNode(node)) {
- event.dataTransfer.effectAllowed = "copyLink";
- break;
- }
- }
-
- this._controller.setDataTransfer(event);
- event.stopPropagation();
- ]]></handler>
-
- <handler event="dragover"><![CDATA[
- if (event.target.localName != "treechildren")
- return;
-
- let cell = this.treeBoxObject.getCellAt(event.clientX, event.clientY);
- let node = cell.row != -1 ?
- this.view.nodeForTreeIndex(cell.row) :
- this.result.root;
- // cache the dropTarget for the view
- PlacesControllerDragHelper.currentDropTarget = node;
-
- // We have to calculate the orientation since view.canDrop will use
- // it and we want to be consistent with the dropfeedback.
- let tbo = this.treeBoxObject;
- let rowHeight = tbo.rowHeight;
- let eventY = event.clientY - tbo.treeBody.boxObject.y -
- rowHeight * (cell.row - tbo.getFirstVisibleRow());
-
- let orientation = Ci.nsITreeView.DROP_BEFORE;
-
- if (cell.row == -1) {
- // If the row is not valid we try to insert inside the resultNode.
- orientation = Ci.nsITreeView.DROP_ON;
- }
- else if (PlacesUtils.nodeIsContainer(node) &&
- eventY > rowHeight * 0.75) {
- // If we are below the 75% of a container the treeview we try
- // to drop after the node.
- orientation = Ci.nsITreeView.DROP_AFTER;
- }
- else if (PlacesUtils.nodeIsContainer(node) &&
- eventY > rowHeight * 0.25) {
- // If we are below the 25% of a container the treeview we try
- // to drop inside the node.
- orientation = Ci.nsITreeView.DROP_ON;
- }
-
- if (!this.view.canDrop(cell.row, orientation, event.dataTransfer))
- return;
-
- event.preventDefault();
- event.stopPropagation();
- ]]></handler>
-
- <handler event="dragend"><![CDATA[
- PlacesControllerDragHelper.currentDropTarget = null;
- ]]></handler>
-
- </handlers>
- </binding>
-
-</bindings>
diff --git a/components/places/content/treeView.js b/components/places/content/treeView.js
deleted file mode 100644
index aba7314..0000000
--- a/components/places/content/treeView.js
+++ /dev/null
@@ -1,1770 +0,0 @@
-/* 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/. */
-
-Components.utils.import('resource://gre/modules/XPCOMUtils.jsm');
-
-const PTV_interfaces = [Ci.nsITreeView,
- Ci.nsINavHistoryResultObserver,
- Ci.nsINavHistoryResultTreeViewer,
- Ci.nsISupportsWeakReference];
-
-function PlacesTreeView(aFlatList, aOnOpenFlatContainer, aController) {
- this._tree = null;
- this._result = null;
- this._selection = null;
- this._rootNode = null;
- this._rows = [];
- this._flatList = aFlatList;
- this._openContainerCallback = aOnOpenFlatContainer;
- this._controller = aController;
-}
-
-PlacesTreeView.prototype = {
- get wrappedJSObject() this,
-
- __dateService: null,
- get _dateService() {
- if (!this.__dateService) {
- this.__dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"].
- getService(Ci.nsIScriptableDateFormat);
- }
- return this.__dateService;
- },
-
- QueryInterface: XPCOMUtils.generateQI(PTV_interfaces),
-
- // Bug 761494:
- // ----------
- // Some addons use methods from nsINavHistoryResultObserver and
- // nsINavHistoryResultTreeViewer, without QIing to these interfaces first.
- // That's not a problem when the view is retrieved through the
- // <tree>.view getter (which returns the wrappedJSObject of this object),
- // it raises an issue when the view retrieved through the treeBoxObject.view
- // getter. Thus, to avoid breaking addons, the interfaces are prefetched.
- classInfo: XPCOMUtils.generateCI({ interfaces: PTV_interfaces }),
-
- /**
- * This is called once both the result and the tree are set.
- */
- _finishInit: function PTV__finishInit() {
- let selection = this.selection;
- if (selection)
- selection.selectEventsSuppressed = true;
-
- if (!this._rootNode.containerOpen) {
- // This triggers containerStateChanged which then builds the visible
- // section.
- this._rootNode.containerOpen = true;
- }
- else
- this.invalidateContainer(this._rootNode);
-
- // "Activate" the sorting column and update commands.
- this.sortingChanged(this._result.sortingMode);
-
- if (selection)
- selection.selectEventsSuppressed = false;
- },
-
- /**
- * Plain Container: container result nodes which may never include sub
- * hierarchies.
- *
- * When the rows array is constructed, we don't set the children of plain
- * containers. Instead, we keep placeholders for these children. We then
- * build these children lazily as the tree asks us for information about each
- * row. Luckily, the tree doesn't ask about rows outside the visible area.
- *
- * @see _getNodeForRow and _getRowForNode for the actual magic.
- *
- * @note It's guaranteed that all containers are listed in the rows
- * elements array. It's also guaranteed that separators (if they're not
- * filtered, see below) are listed in the visible elements array, because
- * bookmark folders are never built lazily, as described above.
- *
- * @param aContainer
- * A container result node.
- *
- * @return true if aContainer is a plain container, false otherwise.
- */
- _isPlainContainer: function PTV__isPlainContainer(aContainer) {
- // Livemarks are always plain containers.
- if (this._controller.hasCachedLivemarkInfo(aContainer))
- return true;
-
- // We don't know enough about non-query containers.
- if (!(aContainer instanceof Ci.nsINavHistoryQueryResultNode))
- return false;
-
- switch (aContainer.queryOptions.resultType) {
- case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY:
- case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY:
- case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY:
- case Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY:
- return false;
- }
-
- // If it's a folder, it's not a plain container.
- let nodeType = aContainer.type;
- return nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER &&
- nodeType != Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT;
- },
-
- /**
- * Gets the row number for a given node. Assumes that the given node is
- * visible (i.e. it's not an obsolete node).
- *
- * @param aNode
- * A result node. Do not pass an obsolete node, or any
- * node which isn't supposed to be in the tree (e.g. separators in
- * sorted trees).
- * @param [optional] aForceBuild
- * @see _isPlainContainer.
- * If true, the row will be computed even if the node still isn't set
- * in our rows array.
- * @param [optional] aParentRow
- * The row of aNode's parent. Ignored for the root node.
- * @param [optional] aNodeIndex
- * The index of aNode in its parent. Only used if aParentRow is
- * set too.
- *
- * @throws if aNode is invisible.
- * @note If aParentRow and aNodeIndex are passed and parent is a plain
- * container, this method will just return a calculated row value, without
- * making assumptions on existence of the node at that position.
- * @return aNode's row if it's in the rows list or if aForceBuild is set, -1
- * otherwise.
- */
- _getRowForNode:
- function PTV__getRowForNode(aNode, aForceBuild, aParentRow, aNodeIndex) {
- if (aNode == this._rootNode)
- throw new Error("The root node is never visible");
-
- // A node is removed form the view either if it has no parent or if its
- // root-ancestor is not the root node (in which case that's the node
- // for which nodeRemoved was called).
- // Tycho: let ancestors = [x for (x of PlacesUtils.nodeAncestors(aNode))];
- let ancestors = [];
- for (let x of PlacesUtils.nodeAncestors(aNode)) {
- ancestors.push(x);
- }
-
- if (ancestors.length == 0 ||
- ancestors[ancestors.length - 1] != this._rootNode) {
- throw new Error("Removed node passed to _getRowForNode");
- }
-
- // Ensure that the entire chain is open, otherwise that node is invisible.
- for (let ancestor of ancestors) {
- if (!ancestor.containerOpen)
- throw new Error("Invisible node passed to _getRowForNode");
- }
-
- // Non-plain containers are initially built with their contents.
- let parent = aNode.parent;
- let parentIsPlain = this._isPlainContainer(parent);
- if (!parentIsPlain) {
- if (parent == this._rootNode)
- return this._rows.indexOf(aNode);
-
- return this._rows.indexOf(aNode, aParentRow);
- }
-
- let row = -1;
- let useNodeIndex = typeof(aNodeIndex) == "number";
- if (parent == this._rootNode) {
- if (aNode instanceof Ci.nsINavHistoryResultNode) {
- row = useNodeIndex ? aNodeIndex : this._rootNode.getChildIndex(aNode);
- }
- } else if (useNodeIndex && typeof(aParentRow) == "number") {
- // If we have both the row of the parent node, and the node's index, we
- // can avoid searching the rows array if the parent is a plain container.
- row = aParentRow + aNodeIndex + 1;
- } else {
- // Look for the node in the nodes array. Start the search at the parent
- // row. If the parent row isn't passed, we'll pass undefined to indexOf,
- // which is fine.
- row = this._rows.indexOf(aNode, aParentRow);
- if (row == -1 && aForceBuild) {
- let parentRow = typeof(aParentRow) == "number" ? aParentRow
- : this._getRowForNode(parent);
- row = parentRow + parent.getChildIndex(aNode) + 1;
- }
- }
-
- if (row != -1)
- this._rows[row] = aNode;
-
- return row;
- },
-
- /**
- * Given a row, finds and returns the parent details of the associated node.
- *
- * @param aChildRow
- * Row number.
- * @return [parentNode, parentRow]
- */
- _getParentByChildRow: function PTV__getParentByChildRow(aChildRow) {
- let node = this._getNodeForRow(aChildRow);
- let parent = (node === null) ? this._rootNode : node.parent;
-
- // The root node is never visible
- if (parent == this._rootNode)
- return [this._rootNode, -1];
-
- let parentRow = this._rows.lastIndexOf(parent, aChildRow - 1);
- return [parent, parentRow];
- },
-
- /**
- * Gets the node at a given row.
- */
- _getNodeForRow: function PTV__getNodeForRow(aRow) {
- if (aRow < 0) {
- return null;
- }
-
- let node = this._rows[aRow];
- if (node !== undefined)
- return node;
-
- // Find the nearest node.
- let rowNode, row;
- for (let i = aRow - 1; i >= 0 && rowNode === undefined; i--) {
- rowNode = this._rows[i];
- row = i;
- }
-
- // If there's no container prior to the given row, it's a child of
- // the root node (remember: all containers are listed in the rows array).
- if (!rowNode)
- return this._rows[aRow] = this._rootNode.getChild(aRow);
-
- // Unset elements may exist only in plain containers. Thus, if the nearest
- // node is a container, it's the row's parent, otherwise, it's a sibling.
- if (rowNode instanceof Ci.nsINavHistoryContainerResultNode)
- return this._rows[aRow] = rowNode.getChild(aRow - row - 1);
-
- let [parent, parentRow] = this._getParentByChildRow(row);
- return this._rows[aRow] = parent.getChild(aRow - parentRow - 1);
- },
-
- /**
- * This takes a container and recursively appends our rows array per its
- * contents. Assumes that the rows arrays has no rows for the given
- * container.
- *
- * @param [in] aContainer
- * A container result node.
- * @param [in] aFirstChildRow
- * The first row at which nodes may be inserted to the row array.
- * In other words, that's aContainer's row + 1.
- * @param [out] aToOpen
- * An array of containers to open once the build is done.
- *
- * @return the number of rows which were inserted.
- */
- _buildVisibleSection:
- function PTV__buildVisibleSection(aContainer, aFirstChildRow, aToOpen)
- {
- // There's nothing to do if the container is closed.
- if (!aContainer.containerOpen)
- return 0;
-
- // Inserting the new elements into the rows array in one shot (by
- // Array.concat) is faster than resizing the array (by splice) on each loop
- // iteration.
- let cc = aContainer.childCount;
- let newElements = new Array(cc);
- this._rows = this._rows.splice(0, aFirstChildRow)
- .concat(newElements, this._rows);
-
- if (this._isPlainContainer(aContainer))
- return cc;
-
- const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open");
- const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true");
- let sortingMode = this._result.sortingMode;
-
- let rowsInserted = 0;
- for (let i = 0; i < cc; i++) {
- let curChild = aContainer.getChild(i);
- let curChildType = curChild.type;
-
- let row = aFirstChildRow + rowsInserted;
-
- // Don't display separators when sorted.
- if (curChildType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
- if (sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
- // Remove the element for the filtered separator.
- // Notice that the rows array was initially resized to include all
- // children.
- this._rows.splice(row, 1);
- continue;
- }
- }
-
- this._rows[row] = curChild;
- rowsInserted++;
-
- // Recursively do containers.
- if (!this._flatList &&
- curChild instanceof Ci.nsINavHistoryContainerResultNode &&
- !this._controller.hasCachedLivemarkInfo(curChild)) {
- let resource = this._getResourceForNode(curChild);
- let isopen = resource != null &&
- PlacesUIUtils.localStore.HasAssertion(resource,
- openLiteral,
- trueLiteral, true);
- if (isopen != curChild.containerOpen)
- aToOpen.push(curChild);
- else if (curChild.containerOpen && curChild.childCount > 0)
- rowsInserted += this._buildVisibleSection(curChild, row + 1, aToOpen);
- }
- }
-
- return rowsInserted;
- },
-
- /**
- * This counts how many rows a node takes in the tree. For containers it
- * will count the node itself plus any child node following it.
- */
- _countVisibleRowsForNodeAtRow:
- function PTV__countVisibleRowsForNodeAtRow(aNodeRow) {
- let node = this._rows[aNodeRow];
-
- // If it's not listed yet, we know that it's a leaf node (instanceof also
- // null-checks).
- if (!(node instanceof Ci.nsINavHistoryContainerResultNode))
- return 1;
-
- let outerLevel = node.indentLevel;
- for (let i = aNodeRow + 1; i < this._rows.length; i++) {
- let rowNode = this._rows[i];
- if (rowNode && rowNode.indentLevel <= outerLevel)
- return i - aNodeRow;
- }
-
- // This node plus its children take up the bottom of the list.
- return this._rows.length - aNodeRow;
- },
-
- _getSelectedNodesInRange:
- function PTV__getSelectedNodesInRange(aFirstRow, aLastRow) {
- let selection = this.selection;
- let rc = selection.getRangeCount();
- if (rc == 0)
- return [];
-
- // The visible-area borders are needed for checking whether a
- // selected row is also visible.
- let firstVisibleRow = this._tree.getFirstVisibleRow();
- let lastVisibleRow = this._tree.getLastVisibleRow();
-
- let nodesInfo = [];
- for (let rangeIndex = 0; rangeIndex < rc; rangeIndex++) {
- let min = { }, max = { };
- selection.getRangeAt(rangeIndex, min, max);
-
- // If this range does not overlap the replaced chunk, we don't need to
- // persist the selection.
- if (max.value < aFirstRow || min.value > aLastRow)
- continue;
-
- let firstRow = Math.max(min.value, aFirstRow);
- let lastRow = Math.min(max.value, aLastRow);
- for (let i = firstRow; i <= lastRow; i++) {
- nodesInfo.push({
- node: this._rows[i],
- oldRow: i,
- wasVisible: i >= firstVisibleRow && i <= lastVisibleRow
- });
- }
- }
-
- return nodesInfo;
- },
-
- /**
- * Tries to find an equivalent node for a node which was removed. We first
- * look for the original node, in case it was just relocated. Then, if we
- * that node was not found, we look for a node that has the same itemId, uri
- * and time values.
- *
- * @param aUpdatedContainer
- * An ancestor of the node which was removed. It does not have to be
- * its direct parent.
- * @param aOldNode
- * The node which was removed.
- *
- * @return the row number of an equivalent node for aOldOne, if one was
- * found, -1 otherwise.
- */
- _getNewRowForRemovedNode:
- function PTV__getNewRowForRemovedNode(aUpdatedContainer, aOldNode) {
- if (aOldNode == undefined) {
- return -1;
- }
- let parent = aOldNode.parent;
- if (parent) {
- // If the node's parent is still set, the node is not obsolete
- // and we should just find out its new position.
- // However, if any of the node's ancestor is closed, the node is
- // invisible.
- let ancestors = PlacesUtils.nodeAncestors(aOldNode);
- for (let ancestor of ancestors) {
- if (!ancestor.containerOpen)
- return -1;
- }
-
- return this._getRowForNode(aOldNode, true);
- }
-
- // There's a broken edge case here.
- // If a visit appears in two queries, and the second one was
- // the old node, we'll select the first one after refresh. There's
- // nothing we could do about that, because aOldNode.parent is
- // gone by the time invalidateContainer is called.
- let newNode = aUpdatedContainer.findNodeByDetails(aOldNode.uri,
- aOldNode.time,
- aOldNode.itemId,
- true);
- if (!newNode)
- return -1;
-
- return this._getRowForNode(newNode, true);
- },
-
- /**
- * Restores a given selection state as near as possible to the original
- * selection state.
- *
- * @param aNodesInfo
- * The persisted selection state as returned by
- * _getSelectedNodesInRange.
- * @param aUpdatedContainer
- * The container which was updated.
- */
- _restoreSelection:
- function PTV__restoreSelection(aNodesInfo, aUpdatedContainer) {
- if (aNodesInfo.length == 0)
- return;
-
- let selection = this.selection;
-
- // Attempt to ensure that previously-visible selection will be visible
- // if it's re-selected. However, we can only ensure that for one row.
- let scrollToRow = -1;
- for (let i = 0; i < aNodesInfo.length; i++) {
- let nodeInfo = aNodesInfo[i];
- let row = this._getNewRowForRemovedNode(aUpdatedContainer,
- nodeInfo.node);
- // Select the found node, if any.
- if (row != -1) {
- selection.rangedSelect(row, row, true);
- if (nodeInfo.wasVisible && scrollToRow == -1)
- scrollToRow = row;
- }
- }
-
- // If only one node was previously selected and there's no selection now,
- // select the node at its old row, if any.
- if (aNodesInfo.length == 1 && selection.count == 0) {
- let row = Math.min(aNodesInfo[0].oldRow, this._rows.length - 1);
- if (row != -1) {
- selection.rangedSelect(row, row, true);
- if (aNodesInfo[0].wasVisible && scrollToRow == -1)
- scrollToRow = aNodesInfo[0].oldRow;
- }
- }
-
- if (scrollToRow != -1)
- this._tree.ensureRowIsVisible(scrollToRow);
- },
-
- _convertPRTimeToString: function PTV__convertPRTimeToString(aTime) {
- const MS_PER_MINUTE = 60000;
- const MS_PER_DAY = 86400000;
- let timeMs = aTime / 1000; // PRTime is in microseconds
-
- // Date is calculated starting from midnight, so the modulo with a day are
- // milliseconds from today's midnight.
- // getTimezoneOffset corrects that based on local time, notice midnight
- // can have a different offset during DST-change days.
- let dateObj = new Date();
- let now = dateObj.getTime() - dateObj.getTimezoneOffset() * MS_PER_MINUTE;
- let midnight = now - (now % MS_PER_DAY);
- midnight += new Date(midnight).getTimezoneOffset() * MS_PER_MINUTE;
-
- let dateFormat = timeMs >= midnight ?
- Ci.nsIScriptableDateFormat.dateFormatNone :
- Ci.nsIScriptableDateFormat.dateFormatShort;
-
- let timeObj = new Date(timeMs);
- return (this._dateService.FormatDateTime("", dateFormat,
- Ci.nsIScriptableDateFormat.timeFormatNoSeconds,
- timeObj.getFullYear(), timeObj.getMonth() + 1,
- timeObj.getDate(), timeObj.getHours(),
- timeObj.getMinutes(), timeObj.getSeconds()));
- },
-
- COLUMN_TYPE_UNKNOWN: 0,
- COLUMN_TYPE_TITLE: 1,
- COLUMN_TYPE_URI: 2,
- COLUMN_TYPE_DATE: 3,
- COLUMN_TYPE_VISITCOUNT: 4,
- COLUMN_TYPE_KEYWORD: 5,
- COLUMN_TYPE_DESCRIPTION: 6,
- COLUMN_TYPE_DATEADDED: 7,
- COLUMN_TYPE_LASTMODIFIED: 8,
- COLUMN_TYPE_TAGS: 9,
- COLUMN_TYPE_PARENTFOLDER: 10,
- COLUMN_TYPE_PARENTFOLDERPATH: 11,
-
- _getColumnType: function PTV__getColumnType(aColumn) {
- let columnType = aColumn.element.getAttribute("anonid") || aColumn.id;
-
- switch (columnType) {
- case "title":
- return this.COLUMN_TYPE_TITLE;
- case "url":
- return this.COLUMN_TYPE_URI;
- case "date":
- return this.COLUMN_TYPE_DATE;
- case "visitCount":
- return this.COLUMN_TYPE_VISITCOUNT;
- case "keyword":
- return this.COLUMN_TYPE_KEYWORD;
- case "description":
- return this.COLUMN_TYPE_DESCRIPTION;
- case "dateAdded":
- return this.COLUMN_TYPE_DATEADDED;
- case "lastModified":
- return this.COLUMN_TYPE_LASTMODIFIED;
- case "tags":
- return this.COLUMN_TYPE_TAGS;
- case "parentFolder":
- return this.COLUMN_TYPE_PARENTFOLDER;
- case "parentFolderPath":
- return this.COLUMN_TYPE_PARENTFOLDERPATH;
- }
- return this.COLUMN_TYPE_UNKNOWN;
- },
-
- _sortTypeToColumnType: function PTV__sortTypeToColumnType(aSortType) {
- switch (aSortType) {
- case Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_ASCENDING:
- return [this.COLUMN_TYPE_TITLE, false];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_TITLE_DESCENDING:
- return [this.COLUMN_TYPE_TITLE, true];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_ASCENDING:
- return [this.COLUMN_TYPE_DATE, false];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING:
- return [this.COLUMN_TYPE_DATE, true];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_URI_ASCENDING:
- return [this.COLUMN_TYPE_URI, false];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_URI_DESCENDING:
- return [this.COLUMN_TYPE_URI, true];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_ASCENDING:
- return [this.COLUMN_TYPE_VISITCOUNT, false];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING:
- return [this.COLUMN_TYPE_VISITCOUNT, true];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_KEYWORD_ASCENDING:
- return [this.COLUMN_TYPE_KEYWORD, false];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_KEYWORD_DESCENDING:
- return [this.COLUMN_TYPE_KEYWORD, true];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_ASCENDING:
- if (this._result.sortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO)
- return [this.COLUMN_TYPE_DESCRIPTION, false];
- break;
- case Ci.nsINavHistoryQueryOptions.SORT_BY_ANNOTATION_DESCENDING:
- if (this._result.sortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO)
- return [this.COLUMN_TYPE_DESCRIPTION, true];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_ASCENDING:
- return [this.COLUMN_TYPE_DATEADDED, false];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING:
- return [this.COLUMN_TYPE_DATEADDED, true];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_ASCENDING:
- return [this.COLUMN_TYPE_LASTMODIFIED, false];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING:
- return [this.COLUMN_TYPE_LASTMODIFIED, true];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_TAGS_ASCENDING:
- return [this.COLUMN_TYPE_TAGS, false];
- case Ci.nsINavHistoryQueryOptions.SORT_BY_TAGS_DESCENDING:
- return [this.COLUMN_TYPE_TAGS, true];
- }
- return [this.COLUMN_TYPE_UNKNOWN, false];
- },
-
- // nsINavHistoryResultObserver
- nodeInserted: function PTV_nodeInserted(aParentNode, aNode, aNewIndex) {
- NS_ASSERT(this._result, "Got a notification but have no result!");
- if (!this._tree || !this._result)
- return;
-
- // Bail out for hidden separators.
- if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
- return;
-
- let parentRow;
- if (aParentNode != this._rootNode) {
- parentRow = this._getRowForNode(aParentNode);
-
- // Update parent when inserting the first item, since twisty has changed.
- if (aParentNode.childCount == 1)
- this._tree.invalidateRow(parentRow);
- }
-
- // Compute the new row number of the node.
- let row = -1;
- let cc = aParentNode.childCount;
- if (aNewIndex == 0 || this._isPlainContainer(aParentNode) || cc == 0) {
- // We don't need to worry about sub hierarchies of the parent node
- // if it's a plain container, or if the new node is its first child.
- if (aParentNode == this._rootNode)
- row = aNewIndex;
- else
- row = parentRow + aNewIndex + 1;
- }
- else {
- // Here, we try to find the next visible element in the child list so we
- // can set the new visible index to be right before that. Note that we
- // have to search down instead of up, because some siblings could have
- // children themselves that would be in the way.
- let separatorsAreHidden = PlacesUtils.nodeIsSeparator(aNode) &&
- this.isSorted();
- for (let i = aNewIndex + 1; i < cc; i++) {
- let node = aParentNode.getChild(i);
- if (!separatorsAreHidden || PlacesUtils.nodeIsSeparator(node)) {
- // The children have not been shifted so the next item will have what
- // should be our index.
- row = this._getRowForNode(node, false, parentRow, i);
- break;
- }
- }
- if (row < 0) {
- // At the end of the child list without finding a visible sibling. This
- // is a little harder because we don't know how many rows the last item
- // in our list takes up (it could be a container with many children).
- let prevChild = aParentNode.getChild(aNewIndex - 1);
- let prevIndex = this._getRowForNode(prevChild, false, parentRow,
- aNewIndex - 1);
- row = prevIndex + this._countVisibleRowsForNodeAtRow(prevIndex);
- }
- }
-
- this._rows.splice(row, 0, aNode);
- this._tree.rowCountChanged(row, 1);
-
- if (PlacesUtils.nodeIsContainer(aNode) &&
- PlacesUtils.asContainer(aNode).containerOpen) {
- this.invalidateContainer(aNode);
- }
- },
-
- /**
- * THIS FUNCTION DOES NOT HANDLE cases where a collapsed node is being
- * removed but the node it is collapsed with is not being removed (this then
- * just swap out the removee with its collapsing partner). The only time
- * when we really remove things is when deleting URIs, which will apply to
- * all collapsees. This function is called sometimes when resorting items.
- * However, we won't do this when sorted by date because dates will never
- * change for visits, and date sorting is the only time things are collapsed.
- */
- nodeRemoved: function PTV_nodeRemoved(aParentNode, aNode, aOldIndex) {
- NS_ASSERT(this._result, "Got a notification but have no result!");
- if (!this._tree || !this._result)
- return;
-
- // XXX bug 517701: We don't know what to do when the root node is removed.
- if (aNode == this._rootNode)
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-
- // Bail out for hidden separators.
- if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
- return;
-
- let parentRow = aParentNode == this._rootNode ?
- undefined : this._getRowForNode(aParentNode, true);
- let oldRow = this._getRowForNode(aNode, true, parentRow, aOldIndex);
- if (oldRow < 0)
- throw Cr.NS_ERROR_UNEXPECTED;
-
- // If the node was exclusively selected, the node next to it will be
- // selected.
- let selectNext = false;
- let selection = this.selection;
- if (selection.getRangeCount() == 1) {
- let min = { }, max = { };
- selection.getRangeAt(0, min, max);
- if (min.value == max.value &&
- this.nodeForTreeIndex(min.value) == aNode)
- selectNext = true;
- }
-
- // Remove the node and its children, if any.
- let count = this._countVisibleRowsForNodeAtRow(oldRow);
- this._rows.splice(oldRow, count);
- this._tree.rowCountChanged(oldRow, -count);
-
- // Redraw the parent if its twisty state has changed.
- if (aParentNode != this._rootNode && !aParentNode.hasChildren) {
- let parentRow = oldRow - 1;
- this._tree.invalidateRow(parentRow);
- }
-
- // Restore selection if the node was exclusively selected.
- if (!selectNext)
- return;
-
- // Restore selection.
- let rowToSelect = Math.min(oldRow, this._rows.length - 1);
- if (rowToSelect != -1)
- this.selection.rangedSelect(rowToSelect, rowToSelect, true);
- },
-
- nodeMoved:
- function PTV_nodeMoved(aNode, aOldParent, aOldIndex, aNewParent, aNewIndex) {
- NS_ASSERT(this._result, "Got a notification but have no result!");
- if (!this._tree || !this._result)
- return;
-
- // Bail out for hidden separators.
- if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted())
- return;
-
- // Note that at this point the node has already been moved by the backend,
- // so we must give hints to _getRowForNode to get the old row position.
- let oldParentRow = aOldParent == this._rootNode ?
- undefined : this._getRowForNode(aOldParent, true);
- let oldRow = this._getRowForNode(aNode, true, oldParentRow, aOldIndex);
- if (oldRow < 0)
- throw Cr.NS_ERROR_UNEXPECTED;
-
- // If this node is a container it could take up more than one row.
- let count = this._countVisibleRowsForNodeAtRow(oldRow);
-
- // Persist selection state.
- let nodesToReselect =
- this._getSelectedNodesInRange(oldRow, oldRow + count);
- if (nodesToReselect.length > 0)
- this.selection.selectEventsSuppressed = true;
-
- // Redraw the parent if its twisty state has changed.
- if (aOldParent != this._rootNode && !aOldParent.hasChildren) {
- let parentRow = oldRow - 1;
- this._tree.invalidateRow(parentRow);
- }
-
- // Remove node and its children, if any, from the old position.
- this._rows.splice(oldRow, count);
- this._tree.rowCountChanged(oldRow, -count);
-
- // Insert the node into the new position.
- this.nodeInserted(aNewParent, aNode, aNewIndex);
-
- // Restore selection.
- if (nodesToReselect.length > 0) {
- this._restoreSelection(nodesToReselect, aNewParent);
- this.selection.selectEventsSuppressed = false;
- }
- },
-
- _invalidateCellValue: function PTV__invalidateCellValue(aNode,
- aColumnType) {
- NS_ASSERT(this._result, "Got a notification but have no result!");
- if (!this._tree || !this._result)
- return;
-
- // Nothing to do for the root node.
- if (aNode == this._rootNode)
- return;
-
- let row = this._getRowForNode(aNode);
- if (row == -1)
- return;
-
- let column = this._findColumnByType(aColumnType);
- if (column && !column.element.hidden)
- this._tree.invalidateCell(row, column);
-
- // Last modified time is altered for almost all node changes.
- if (aColumnType != this.COLUMN_TYPE_LASTMODIFIED) {
- let lastModifiedColumn =
- this._findColumnByType(this.COLUMN_TYPE_LASTMODIFIED);
- if (lastModifiedColumn && !lastModifiedColumn.hidden)
- this._tree.invalidateCell(row, lastModifiedColumn);
- }
- },
-
- _populateLivemarkContainer: function PTV__populateLivemarkContainer(aNode) {
- PlacesUtils.livemarks.getLivemark({ id: aNode.itemId })
- .then(aLivemark => {
- let placesNode = aNode;
- // Need to check containerOpen since getLivemark is async.
- if (!placesNode.containerOpen)
- return;
-
- let children = aLivemark.getNodesForContainer(placesNode);
- for (let i = 0; i < children.length; i++) {
- let child = children[i];
- this.nodeInserted(placesNode, child, i);
- }
- }, Components.utils.reportError);
- },
-
- nodeTitleChanged: function PTV_nodeTitleChanged(aNode, aNewTitle) {
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
- },
-
- nodeURIChanged: function PTV_nodeURIChanged(aNode, aNewURI) {
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_URI);
- },
-
- nodeIconChanged: function PTV_nodeIconChanged(aNode) {
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
- },
-
- nodeHistoryDetailsChanged:
- function PTV_nodeHistoryDetailsChanged(aNode, aUpdatedVisitDate,
- aUpdatedVisitCount) {
- if (aNode.parent && this._controller.hasCachedLivemarkInfo(aNode.parent)) {
- // Find the node in the parent.
- let parentRow = this._flatList ? 0 : this._getRowForNode(aNode.parent);
- for (let i = parentRow; i < this._rows.length; i++) {
- let child = this.nodeForTreeIndex(i);
- if (child.uri == aNode.uri) {
- this._cellProperties.delete(child);
- this._invalidateCellValue(child, this.COLUMN_TYPE_TITLE);
- break;
- }
- }
- return;
- }
-
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATE);
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_VISITCOUNT);
- },
-
- nodeTagsChanged: function PTV_nodeTagsChanged(aNode) {
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_TAGS);
- },
-
- nodeKeywordChanged: function PTV_nodeKeywordChanged(aNode, aNewKeyword) {
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_KEYWORD);
- },
-
- nodeAnnotationChanged: function PTV_nodeAnnotationChanged(aNode, aAnno) {
- if (aAnno == PlacesUIUtils.DESCRIPTION_ANNO) {
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_DESCRIPTION);
- }
- else if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
- PlacesUtils.livemarks.getLivemark({ id: aNode.itemId })
- .then(aLivemark => {
- this._controller.cacheLivemarkInfo(aNode, aLivemark);
- let properties = this._cellProperties.get(aNode);
- this._cellProperties.set(aNode, properties += " livemark");
- // The livemark attribute is set as a cell property on the title cell.
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
- }, Components.utils.reportError);
- }
- },
-
- nodeDateAddedChanged: function PTV_nodeDateAddedChanged(aNode, aNewValue) {
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_DATEADDED);
- },
-
- nodeLastModifiedChanged:
- function PTV_nodeLastModifiedChanged(aNode, aNewValue) {
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_LASTMODIFIED);
- },
-
- containerStateChanged:
- function PTV_containerStateChanged(aNode, aOldState, aNewState) {
- this.invalidateContainer(aNode);
-
- if (PlacesUtils.nodeIsFolder(aNode) ||
- (this._flatList && aNode == this._rootNode)) {
- let queryOptions = PlacesUtils.asQuery(this._rootNode).queryOptions;
- if (queryOptions.excludeItems) {
- return;
- }
- if (aNode.itemId != -1) { // run when there's a valid node id
- PlacesUtils.livemarks.getLivemark({ id: aNode.itemId })
- .then(aLivemark => {
- let shouldInvalidate =
- !this._controller.hasCachedLivemarkInfo(aNode);
- this._controller.cacheLivemarkInfo(aNode, aLivemark);
- if (aNewState == Components.interfaces.nsINavHistoryContainerResultNode.STATE_OPENED) {
- aLivemark.registerForUpdates(aNode, this);
- // Prioritize the current livemark.
- aLivemark.reload();
- PlacesUtils.livemarks.reloadLivemarks();
- if (shouldInvalidate)
- this.invalidateContainer(aNode);
- }
- else {
- aLivemark.unregisterForUpdates(aNode);
- }
- }, () => undefined);
- }
- }
- },
-
- invalidateContainer: function PTV_invalidateContainer(aContainer) {
- NS_ASSERT(this._result, "Need to have a result to update");
- if (!this._tree)
- return;
-
- let startReplacement, replaceCount;
- if (aContainer == this._rootNode) {
- startReplacement = 0;
- replaceCount = this._rows.length;
-
- // If the root node is now closed, the tree is empty.
- if (!this._rootNode.containerOpen) {
- this._rows = [];
- if (replaceCount)
- this._tree.rowCountChanged(startReplacement, -replaceCount);
-
- return;
- }
- }
- else {
- // Update the twisty state.
- let row = this._getRowForNode(aContainer);
- this._tree.invalidateRow(row);
-
- // We don't replace the container node itself, so we should decrease the
- // replaceCount by 1.
- startReplacement = row + 1;
- replaceCount = this._countVisibleRowsForNodeAtRow(row) - 1;
- }
-
- // Persist selection state.
- let nodesToReselect =
- this._getSelectedNodesInRange(startReplacement,
- startReplacement + replaceCount);
-
- // Now update the number of elements.
- this.selection.selectEventsSuppressed = true;
-
- // First remove the old elements
- this._rows.splice(startReplacement, replaceCount);
-
- // If the container is now closed, we're done.
- if (!aContainer.containerOpen) {
- let oldSelectionCount = this.selection.count;
- if (replaceCount)
- this._tree.rowCountChanged(startReplacement, -replaceCount);
-
- // Select the row next to the closed container if any of its
- // children were selected, and nothing else is selected.
- if (nodesToReselect.length > 0 &&
- nodesToReselect.length == oldSelectionCount) {
- this.selection.rangedSelect(startReplacement, startReplacement, true);
- this._tree.ensureRowIsVisible(startReplacement);
- }
-
- this.selection.selectEventsSuppressed = false;
- return;
- }
-
- // Otherwise, start a batch first.
- this._tree.beginUpdateBatch();
- if (replaceCount)
- this._tree.rowCountChanged(startReplacement, -replaceCount);
-
- let toOpenElements = [];
- let elementsAddedCount = this._buildVisibleSection(aContainer,
- startReplacement,
- toOpenElements);
- if (elementsAddedCount)
- this._tree.rowCountChanged(startReplacement, elementsAddedCount);
-
- if (!this._flatList) {
- // Now, open any containers that were persisted.
- for (let i = 0; i < toOpenElements.length; i++) {
- let item = toOpenElements[i];
- let parent = item.parent;
-
- // Avoid recursively opening containers.
- while (parent) {
- if (parent.uri == item.uri)
- break;
- parent = parent.parent;
- }
-
- // If we don't have a parent, we made it all the way to the root
- // and didn't find a match, so we can open our item.
- if (!parent && !item.containerOpen)
- item.containerOpen = true;
- }
- }
-
- if (this._controller.hasCachedLivemarkInfo(aContainer)) {
- let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
- if (!queryOptions.excludeItems) {
- this._populateLivemarkContainer(aContainer);
- }
- }
-
- this._tree.endUpdateBatch();
-
- // Restore selection.
- this._restoreSelection(nodesToReselect, aContainer);
- this.selection.selectEventsSuppressed = false;
- },
-
- _columns: [],
- _findColumnByType: function PTV__findColumnByType(aColumnType) {
- if (this._columns[aColumnType])
- return this._columns[aColumnType];
-
- let columns = this._tree.columns;
- let colCount = columns.count;
- for (let i = 0; i < colCount; i++) {
- let column = columns.getColumnAt(i);
- let columnType = this._getColumnType(column);
- this._columns[columnType] = column;
- if (columnType == aColumnType)
- return column;
- }
-
- // That's completely valid. Most of our trees actually include just the
- // title column.
- return null;
- },
-
- sortingChanged: function PTV__sortingChanged(aSortingMode) {
- if (!this._tree || !this._result)
- return;
-
- // Depending on the sort mode, certain commands may be disabled.
- window.updateCommands("sort");
-
- let columns = this._tree.columns;
-
- // Clear old sorting indicator.
- let sortedColumn = columns.getSortedColumn();
- if (sortedColumn)
- sortedColumn.element.removeAttribute("sortDirection");
-
- // Set new sorting indicator by looking through all columns for ours.
- if (aSortingMode == Ci.nsINavHistoryQueryOptions.SORT_BY_NONE)
- return;
-
- let [desiredColumn, desiredIsDescending] =
- this._sortTypeToColumnType(aSortingMode);
- let colCount = columns.count;
- let column = this._findColumnByType(desiredColumn);
- if (column) {
- let sortDir = desiredIsDescending ? "descending" : "ascending";
- column.element.setAttribute("sortDirection", sortDir);
- }
- },
-
- _inBatchMode: false,
- batching: function PTV__batching(aToggleMode) {
- if (this._inBatchMode != aToggleMode) {
- this._inBatchMode = this.selection.selectEventsSuppressed = aToggleMode;
- if (this._inBatchMode) {
- this._tree.beginUpdateBatch();
- }
- else {
- this._tree.endUpdateBatch();
- }
- }
- },
-
- get result() this._result,
- set result(val) {
- if (this._result) {
- this._result.removeObserver(this);
- this._rootNode.containerOpen = false;
- }
-
- if (val) {
- this._result = val;
- this._rootNode = this._result.root;
- this._cellProperties = new Map();
- this._cuttingNodes = new Set();
- }
- else if (this._result) {
- delete this._result;
- delete this._rootNode;
- delete this._cellProperties;
- delete this._cuttingNodes;
- }
-
- // If the tree is not set yet, setTree will call finishInit.
- if (this._tree && val)
- this._finishInit();
-
- return val;
- },
-
- nodeForTreeIndex: function PTV_nodeForTreeIndex(aIndex) {
- if (aIndex > this._rows.length)
- throw Cr.NS_ERROR_INVALID_ARG;
-
- return this._getNodeForRow(aIndex);
- },
-
- treeIndexForNode: function PTV_treeNodeForIndex(aNode) {
- // The API allows passing invisible nodes.
- try {
- return this._getRowForNode(aNode, true);
- }
- catch(ex) { }
-
- return Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE;
- },
-
- _getResourceForNode: function PTV_getResourceForNode(aNode)
- {
- let uri = aNode.uri;
- NS_ASSERT(uri, "if there is no uri, we can't persist the open state");
- return uri ? PlacesUIUtils.RDF.GetResource(uri) : null;
- },
-
- // nsITreeView
- get rowCount() this._rows.length,
- get selection() this._selection,
- set selection(val) this._selection = val,
-
- getRowProperties: function() { return ""; },
-
- getCellProperties:
- function PTV_getCellProperties(aRow, aColumn) {
- // for anonid-trees, we need to add the column-type manually
- var props = "";
- let columnType = aColumn.element.getAttribute("anonid");
- if (columnType)
- props += columnType;
- else
- columnType = aColumn.id;
-
- // Set the "ltr" property on url cells
- if (columnType == "url")
- props += " ltr";
-
- if (columnType != "title")
- return props;
-
- let node = this._getNodeForRow(aRow);
-
- if (this._cuttingNodes.has(node)) {
- props += " cutting";
- }
-
- let properties = this._cellProperties.get(node);
- if (properties === undefined) {
- properties = "";
- let itemId = node.itemId;
- let nodeType = node.type;
- if (PlacesUtils.containerTypes.indexOf(nodeType) != -1) {
- if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY) {
- properties += " query";
- if (PlacesUtils.nodeIsTagQuery(node))
- properties += " tagContainer";
- else if (PlacesUtils.nodeIsDay(node))
- properties += " dayContainer";
- else if (PlacesUtils.nodeIsHost(node))
- properties += " hostContainer";
- }
- else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER ||
- nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT) {
- if (this._controller.hasCachedLivemarkInfo(node)) {
- properties += " livemark";
- }
- else {
- PlacesUtils.livemarks.getLivemark({ id: node.itemId })
- .then(aLivemark => {
- this._controller.cacheLivemarkInfo(node, aLivemark);
- let props = this._cellProperties.get(node);
- this._cellProperties.set(node, props += " livemark");
- // The livemark attribute is set as a cell property on the title cell.
- this._invalidateCellValue(node, this.COLUMN_TYPE_TITLE);
- }, () => undefined);
- }
- }
-
- if (itemId != -1) {
- let queryName = PlacesUIUtils.getLeftPaneQueryNameFromId(itemId);
- if (queryName)
- properties += " OrganizerQuery_" + queryName;
- }
- }
- else if (nodeType == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR)
- properties += " separator";
- else if (PlacesUtils.nodeIsURI(node)) {
- properties += " " + PlacesUIUtils.guessUrlSchemeForUI(node.uri);
-
- if (this._controller.hasCachedLivemarkInfo(node.parent)) {
- properties += " livemarkItem";
- if (node.accessCount) {
- properties += " visited";
- }
- }
- }
-
- this._cellProperties.set(node, properties);
- }
-
- return props + " " + properties;
- },
-
- getColumnProperties: function(aColumn) { return ""; },
-
- isContainer: function PTV_isContainer(aRow) {
- // Only leaf nodes aren't listed in the rows array.
- let node = this._rows[aRow];
- if (node === undefined)
- return false;
-
- if (PlacesUtils.nodeIsContainer(node)) {
- // Flat-lists may ignore expandQueries and other query options when
- // they are asked to open a container.
- if (this._flatList)
- return true;
-
- // treat non-expandable childless queries as non-containers
- if (PlacesUtils.nodeIsQuery(node)) {
- let parent = node.parent;
- if ((PlacesUtils.nodeIsQuery(parent) ||
- PlacesUtils.nodeIsFolder(parent)) &&
- !PlacesUtils.asQuery(node).hasChildren)
- return PlacesUtils.asQuery(parent).queryOptions.expandQueries;
- }
- return true;
- }
- return false;
- },
-
- isContainerOpen: function PTV_isContainerOpen(aRow) {
- if (this._flatList)
- return false;
-
- // All containers are listed in the rows array.
- return this._rows[aRow].containerOpen;
- },
-
- isContainerEmpty: function PTV_isContainerEmpty(aRow) {
- if (this._flatList)
- return true;
-
- let node = this._rows[aRow];
- if (this._controller.hasCachedLivemarkInfo(node)) {
- let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
- return queryOptions.excludeItems;
- }
-
- // All containers are listed in the rows array.
- return !node.hasChildren;
- },
-
- isSeparator: function PTV_isSeparator(aRow) {
- // All separators are listed in the rows array.
- let node = this._rows[aRow];
- return node && PlacesUtils.nodeIsSeparator(node);
- },
-
- isSorted: function PTV_isSorted() {
- return this._result.sortingMode !=
- Ci.nsINavHistoryQueryOptions.SORT_BY_NONE;
- },
-
- canDrop: function PTV_canDrop(aRow, aOrientation, aDataTransfer) {
- if (!this._result)
- throw Cr.NS_ERROR_UNEXPECTED;
-
- // Drop position into a sorted treeview would be wrong.
- if (this.isSorted())
- return false;
-
- let ip = this._getInsertionPoint(aRow, aOrientation);
- return ip && PlacesControllerDragHelper.canDrop(ip, aDataTransfer);
- },
-
- _getInsertionPoint: function PTV__getInsertionPoint(index, orientation) {
- let container = this._result.root;
- let dropNearItemId = -1;
- // When there's no selection, assume the container is the container
- // the view is populated from (i.e. the result's itemId).
- if (index != -1) {
- let lastSelected = this.nodeForTreeIndex(index);
- if (this.isContainer(index) && orientation == Ci.nsITreeView.DROP_ON) {
- // If the last selected item is an open container, append _into_
- // it, rather than insert adjacent to it.
- container = lastSelected;
- index = -1;
- }
- else if (lastSelected.containerOpen &&
- orientation == Ci.nsITreeView.DROP_AFTER &&
- lastSelected.hasChildren) {
- // If the last selected node is an open container and the user is
- // trying to drag into it as a first node, really insert into it.
- container = lastSelected;
- orientation = Ci.nsITreeView.DROP_ON;
- index = 0;
- }
- else {
- // Use the last-selected node's container.
- container = lastSelected.parent;
-
- // During its Drag & Drop operation, the tree code closes-and-opens
- // containers very often (part of the XUL "spring-loaded folders"
- // implementation). And in certain cases, we may reach a closed
- // container here. However, we can simply bail out when this happens,
- // because we would then be back here in less than a millisecond, when
- // the container had been reopened.
- if (!container || !container.containerOpen)
- return null;
-
- // Avoid the potentially expensive call to getChildIndex
- // if we know this container doesn't allow insertion.
- if (PlacesControllerDragHelper.disallowInsertion(container))
- return null;
-
- let queryOptions = PlacesUtils.asQuery(this._result.root).queryOptions;
- if (queryOptions.sortingMode !=
- Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) {
- // If we are within a sorted view, insert at the end.
- index = -1;
- }
- else if (queryOptions.excludeItems ||
- queryOptions.excludeQueries ||
- queryOptions.excludeReadOnlyFolders) {
- // Some item may be invisible, insert near last selected one.
- // We don't replace index here to avoid requests to the db,
- // instead it will be calculated later by the controller.
- index = -1;
- dropNearItemId = lastSelected.itemId;
- }
- else {
- let lsi = container.getChildIndex(lastSelected);
- index = orientation == Ci.nsITreeView.DROP_BEFORE ? lsi : lsi + 1;
- }
- }
- }
-
- if (PlacesControllerDragHelper.disallowInsertion(container))
- return null;
-
- return new InsertionPoint(PlacesUtils.getConcreteItemId(container),
- index, orientation,
- PlacesUtils.nodeIsTagQuery(container),
- dropNearItemId);
- },
-
- drop: function PTV_drop(aRow, aOrientation, aDataTransfer) {
- // We are responsible for translating the |index| and |orientation|
- // parameters into a container id and index within the container,
- // since this information is specific to the tree view.
- let ip = this._getInsertionPoint(aRow, aOrientation);
- if (ip)
- PlacesControllerDragHelper.onDrop(ip, aDataTransfer);
-
- PlacesControllerDragHelper.currentDropTarget = null;
- },
-
- getParentIndex: function PTV_getParentIndex(aRow) {
- let [parentNode, parentRow] = this._getParentByChildRow(aRow);
- return parentRow;
- },
-
- hasNextSibling: function PTV_hasNextSibling(aRow, aAfterIndex) {
- if (aRow == this._rows.length - 1) {
- // The last row has no sibling.
- return false;
- }
-
- let node = this._rows[aRow];
- if (node === undefined || this._isPlainContainer(node.parent)) {
- // The node is a child of a plain container.
- // If the next row is either unset or has the same parent,
- // it's a sibling.
- let nextNode = this._rows[aRow + 1];
- return (nextNode == undefined || nextNode.parent == node.parent);
- }
-
- let thisLevel = node.indentLevel;
- for (let i = aAfterIndex + 1; i < this._rows.length; ++i) {
- let rowNode = this._getNodeForRow(i);
- let nextLevel = rowNode.indentLevel;
- if (nextLevel == thisLevel)
- return true;
- if (nextLevel < thisLevel)
- break;
- }
-
- return false;
- },
-
- getLevel: function(aRow) this._getNodeForRow(aRow).indentLevel,
-
- getImageSrc: function PTV_getImageSrc(aRow, aColumn) {
- // Only the title column has an image.
- if (this._getColumnType(aColumn) != this.COLUMN_TYPE_TITLE)
- return "";
-
- return this._getNodeForRow(aRow).icon;
- },
-
- getProgressMode: function(aRow, aColumn) { },
- getCellValue: function(aRow, aColumn) { },
-
- getCellText: function PTV_getCellText(aRow, aColumn) {
- let node = this._getNodeForRow(aRow);
- switch (this._getColumnType(aColumn)) {
- case this.COLUMN_TYPE_TITLE:
- // normally, this is just the title, but we don't want empty items in
- // the tree view so return a special string if the title is empty.
- // Do it here so that callers can still get at the 0 length title
- // if they go through the "result" API.
- if (PlacesUtils.nodeIsSeparator(node))
- return "";
- return PlacesUIUtils.getBestTitle(node, true);
- case this.COLUMN_TYPE_TAGS:
- return node.tags;
- case this.COLUMN_TYPE_URI:
- if (PlacesUtils.nodeIsURI(node))
- return node.uri;
- return "";
- case this.COLUMN_TYPE_DATE:
- let nodeTime = node.time;
- if (nodeTime == 0 || !PlacesUtils.nodeIsURI(node)) {
- // hosts and days shouldn't have a value for the date column.
- // Actually, you could argue this point, but looking at the
- // results, seeing the most recently visited date is not what
- // I expect, and gives me no information I know how to use.
- // Only show this for URI-based items.
- return "";
- }
-
- return this._convertPRTimeToString(nodeTime);
- case this.COLUMN_TYPE_VISITCOUNT:
- return node.accessCount;
- case this.COLUMN_TYPE_KEYWORD:
- if (PlacesUtils.nodeIsBookmark(node))
- return PlacesUtils.bookmarks.getKeywordForBookmark(node.itemId);
- return "";
- case this.COLUMN_TYPE_DESCRIPTION:
- if (node.itemId != -1) {
- try {
- return PlacesUtils.annotations.
- getItemAnnotation(node.itemId, PlacesUIUtils.DESCRIPTION_ANNO);
- }
- catch (ex) { /* has no description */ }
- }
- return "";
- case this.COLUMN_TYPE_DATEADDED:
- if (node.dateAdded)
- return this._convertPRTimeToString(node.dateAdded);
- return "";
- case this.COLUMN_TYPE_LASTMODIFIED:
- if (node.lastModified)
- return this._convertPRTimeToString(node.lastModified);
- return "";
- case this.COLUMN_TYPE_PARENTFOLDER:
- if (PlacesUtils.nodeIsQuery(node.parent) &&
- PlacesUtils.asQuery(node.parent).queryOptions.queryType ==
- Components.interfaces.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY && node.uri)
- return "";
- var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].
- getService(Components.interfaces.nsINavBookmarksService);
- var rowId = node.itemId;
- try {
- var parentFolderId = bmsvc.getFolderIdForItem(rowId);
- var folderTitle = bmsvc.getItemTitle(parentFolderId);
- } catch(ex) {
- var folderTitle = "";
- }
- return folderTitle;
- case this.COLUMN_TYPE_PARENTFOLDERPATH:
- if (PlacesUtils.nodeIsQuery(node.parent) &&
- PlacesUtils.asQuery(node.parent).queryOptions.queryType ==
- Components.interfaces.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY && node.uri)
- return "";
- var bmsvc = Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].
- getService(Components.interfaces.nsINavBookmarksService);
- var rowId = node.itemId;
- try {
- var FolderId;
- var parentFolderId = bmsvc.getFolderIdForItem(rowId);
- var folderTitle = bmsvc.getItemTitle(parentFolderId);
- while ((FolderId = bmsvc.getFolderIdForItem(parentFolderId))) {
- if (FolderId == parentFolderId)
- break;
- parentFolderId = FolderId;
- var text = bmsvc.getItemTitle(parentFolderId);
- if (!text)
- break;
- folderTitle = text + " /"+ folderTitle;
- }
- folderTitle = folderTitle.replace(/^\s/,"");
- } catch(ex) {
- var folderTitle = "";
- }
- return folderTitle;
- }
- return "";
- },
-
- setTree: function PTV_setTree(aTree) {
- // If we are replacing the tree during a batch, there is a concrete risk
- // that the treeView goes out of sync, thus it's safer to end the batch now.
- // This is a no-op if we are not batching.
- this.batching(false);
-
- let hasOldTree = this._tree != null;
- this._tree = aTree;
-
- if (this._result) {
- if (hasOldTree) {
- // detach from result when we are detaching from the tree.
- // This breaks the reference cycle between us and the result.
- if (!aTree) {
- this._result.removeObserver(this);
- this._rootNode.containerOpen = false;
- }
- }
- if (aTree)
- this._finishInit();
- }
- },
-
- toggleOpenState: function PTV_toggleOpenState(aRow) {
- if (!this._result)
- throw Cr.NS_ERROR_UNEXPECTED;
-
- let node = this._rows[aRow];
- if (this._flatList && this._openContainerCallback) {
- this._openContainerCallback(node);
- return;
- }
-
- // Persist containers open status, but never persist livemarks.
- if (!this._controller.hasCachedLivemarkInfo(node)) {
- let resource = this._getResourceForNode(node);
- if (resource) {
- const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open");
- const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true");
-
- if (node.containerOpen)
- PlacesUIUtils.localStore.Unassert(resource, openLiteral, trueLiteral);
- else
- PlacesUIUtils.localStore.Assert(resource, openLiteral, trueLiteral, true);
- }
- }
-
- node.containerOpen = !node.containerOpen;
- },
-
- cycleHeader: function PTV_cycleHeader(aColumn) {
- if (!this._result)
- throw Cr.NS_ERROR_UNEXPECTED;
-
- // Sometimes you want a tri-state sorting, and sometimes you don't. This
- // rule allows tri-state sorting when the root node is a folder. This will
- // catch the most common cases. When you are looking at folders, you want
- // the third state to reset the sorting to the natural bookmark order. When
- // you are looking at history, that third state has no meaning so we try
- // to disallow it.
- //
- // The problem occurs when you have a query that results in bookmark
- // folders. One example of this is the subscriptions view. In these cases,
- // this rule doesn't allow you to sort those sub-folders by their natural
- // order.
- let allowTriState = PlacesUtils.nodeIsFolder(this._result.root);
-
- let oldSort = this._result.sortingMode;
- let oldSortingAnnotation = this._result.sortingAnnotation;
- let newSort;
- let newSortingAnnotation = "";
- const NHQO = Ci.nsINavHistoryQueryOptions;
- switch (this._getColumnType(aColumn)) {
- case this.COLUMN_TYPE_TITLE:
- if (oldSort == NHQO.SORT_BY_TITLE_ASCENDING)
- newSort = NHQO.SORT_BY_TITLE_DESCENDING;
- else if (allowTriState && oldSort == NHQO.SORT_BY_TITLE_DESCENDING)
- newSort = NHQO.SORT_BY_NONE;
- else
- newSort = NHQO.SORT_BY_TITLE_ASCENDING;
-
- break;
- case this.COLUMN_TYPE_URI:
- if (oldSort == NHQO.SORT_BY_URI_ASCENDING)
- newSort = NHQO.SORT_BY_URI_DESCENDING;
- else if (allowTriState && oldSort == NHQO.SORT_BY_URI_DESCENDING)
- newSort = NHQO.SORT_BY_NONE;
- else
- newSort = NHQO.SORT_BY_URI_ASCENDING;
-
- break;
- case this.COLUMN_TYPE_DATE:
- if (oldSort == NHQO.SORT_BY_DATE_ASCENDING)
- newSort = NHQO.SORT_BY_DATE_DESCENDING;
- else if (allowTriState &&
- oldSort == NHQO.SORT_BY_DATE_DESCENDING)
- newSort = NHQO.SORT_BY_NONE;
- else
- newSort = NHQO.SORT_BY_DATE_ASCENDING;
-
- break;
- case this.COLUMN_TYPE_VISITCOUNT:
- // visit count default is unusual because we sort by descending
- // by default because you are most likely to be looking for
- // highly visited sites when you click it
- if (oldSort == NHQO.SORT_BY_VISITCOUNT_DESCENDING)
- newSort = NHQO.SORT_BY_VISITCOUNT_ASCENDING;
- else if (allowTriState && oldSort == NHQO.SORT_BY_VISITCOUNT_ASCENDING)
- newSort = NHQO.SORT_BY_NONE;
- else
- newSort = NHQO.SORT_BY_VISITCOUNT_DESCENDING;
-
- break;
- case this.COLUMN_TYPE_KEYWORD:
- if (oldSort == NHQO.SORT_BY_KEYWORD_ASCENDING)
- newSort = NHQO.SORT_BY_KEYWORD_DESCENDING;
- else if (allowTriState && oldSort == NHQO.SORT_BY_KEYWORD_DESCENDING)
- newSort = NHQO.SORT_BY_NONE;
- else
- newSort = NHQO.SORT_BY_KEYWORD_ASCENDING;
-
- break;
- case this.COLUMN_TYPE_DESCRIPTION:
- if (oldSort == NHQO.SORT_BY_ANNOTATION_ASCENDING &&
- oldSortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO) {
- newSort = NHQO.SORT_BY_ANNOTATION_DESCENDING;
- newSortingAnnotation = PlacesUIUtils.DESCRIPTION_ANNO;
- }
- else if (allowTriState &&
- oldSort == NHQO.SORT_BY_ANNOTATION_DESCENDING &&
- oldSortingAnnotation == PlacesUIUtils.DESCRIPTION_ANNO)
- newSort = NHQO.SORT_BY_NONE;
- else {
- newSort = NHQO.SORT_BY_ANNOTATION_ASCENDING;
- newSortingAnnotation = PlacesUIUtils.DESCRIPTION_ANNO;
- }
-
- break;
- case this.COLUMN_TYPE_DATEADDED:
- if (oldSort == NHQO.SORT_BY_DATEADDED_ASCENDING)
- newSort = NHQO.SORT_BY_DATEADDED_DESCENDING;
- else if (allowTriState &&
- oldSort == NHQO.SORT_BY_DATEADDED_DESCENDING)
- newSort = NHQO.SORT_BY_NONE;
- else
- newSort = NHQO.SORT_BY_DATEADDED_ASCENDING;
-
- break;
- case this.COLUMN_TYPE_LASTMODIFIED:
- if (oldSort == NHQO.SORT_BY_LASTMODIFIED_ASCENDING)
- newSort = NHQO.SORT_BY_LASTMODIFIED_DESCENDING;
- else if (allowTriState &&
- oldSort == NHQO.SORT_BY_LASTMODIFIED_DESCENDING)
- newSort = NHQO.SORT_BY_NONE;
- else
- newSort = NHQO.SORT_BY_LASTMODIFIED_ASCENDING;
-
- break;
- case this.COLUMN_TYPE_TAGS:
- if (oldSort == NHQO.SORT_BY_TAGS_ASCENDING)
- newSort = NHQO.SORT_BY_TAGS_DESCENDING;
- else if (allowTriState && oldSort == NHQO.SORT_BY_TAGS_DESCENDING)
- newSort = NHQO.SORT_BY_NONE;
- else
- newSort = NHQO.SORT_BY_TAGS_ASCENDING;
-
- break;
- case this.COLUMN_TYPE_PARENTFOLDER:
- return;
-
- break;
- case this.COLUMN_TYPE_PARENTFOLDERPATH:
- return;
-
- break;
- default:
- throw Cr.NS_ERROR_INVALID_ARG;
- }
- this._result.sortingAnnotation = newSortingAnnotation;
- this._result.sortingMode = newSort;
- },
-
- isEditable: function PTV_isEditable(aRow, aColumn) {
- // At this point we only support editing the title field.
- if (aColumn.index != 0)
- return false;
-
- let node = this._rows[aRow];
- if (!node) {
- Cu.reportError("isEditable called for an unbuilt row.");
- return false;
- }
- let itemId = node.itemId;
-
- // Only bookmark-nodes are editable. Fortunately, this check also takes
- // care of livemark children.
- if (itemId == -1)
- return false;
-
- // The following items are also not editable, even though they are bookmark
- // items.
- // * places-roots
- // * the left pane special folders and queries (those are place: uri
- // bookmarks)
- // * separators
- //
- // Note that concrete itemIds aren't used intentionally. For example, we
- // have no reason to disallow renaming a shortcut to the Bookmarks Toolbar,
- // except for the one under All Bookmarks.
- if (PlacesUtils.nodeIsSeparator(node) || PlacesUtils.isRootItem(itemId))
- return false;
-
- let parentId = PlacesUtils.getConcreteItemId(node.parent);
- if (parentId == PlacesUIUtils.leftPaneFolderId ||
- parentId == PlacesUIUtils.allBookmarksFolderId) {
- // Note that the for the time being this is the check that actually
- // blocks renaming places "roots", and not the isRootItem check above.
- // That's because places root are only exposed through folder shortcuts
- // descendants of the left pane folder.
- return false;
- }
-
- return true;
- },
-
- setCellText: function PTV_setCellText(aRow, aColumn, aText) {
- // We may only get here if the cell is editable.
- let node = this._rows[aRow];
- if (node.title != aText) {
- let txn = new PlacesEditItemTitleTransaction(node.itemId, aText);
- PlacesUtils.transactionManager.doTransaction(txn);
- }
- },
-
- toggleCutNode: function PTV_toggleCutNode(aNode, aValue) {
- let currentVal = this._cuttingNodes.has(aNode);
- if (currentVal != aValue) {
- if (aValue)
- this._cuttingNodes.add(aNode);
- else
- this._cuttingNodes.delete(aNode);
-
- this._invalidateCellValue(aNode, this.COLUMN_TYPE_TITLE);
- }
- },
-
- selectionChanged: function() { },
- cycleCell: function(aRow, aColumn) { },
- isSelectable: function(aRow, aColumn) { return false; },
- performAction: function(aAction) { },
- performActionOnRow: function(aAction, aRow) { },
- performActionOnCell: function(aAction, aRow, aColumn) { }
-};
diff --git a/components/places/jar.mn b/components/places/jar.mn
deleted file mode 100644
index 41222e1..0000000
--- a/components/places/jar.mn
+++ /dev/null
@@ -1,34 +0,0 @@
-# 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/.
-
-browser.jar:
-% overlay chrome://browser/content/places/places.xul chrome://browser/content/places/downloadsViewOverlay.xul
-# Provide another URI for the bookmarkProperties dialog so we can persist the
-# attributes separately
- content/browser/places/bookmarkProperties2.xul (content/bookmarkProperties.xul)
-* content/browser/places/places.xul (content/places.xul)
-* content/browser/places/places.js (content/places.js)
- content/browser/places/places.css (content/places.css)
- content/browser/places/organizer.css (content/organizer.css)
- content/browser/places/bookmarkProperties.xul (content/bookmarkProperties.xul)
- content/browser/places/bookmarkProperties.js (content/bookmarkProperties.js)
- content/browser/places/placesOverlay.xul (content/placesOverlay.xul)
-* content/browser/places/menu.xml (content/menu.xml)
- content/browser/places/tree.xml (content/tree.xml)
- content/browser/places/controller.js (content/controller.js)
- content/browser/places/treeView.js (content/treeView.js)
-* content/browser/places/browserPlacesViews.js (content/browserPlacesViews.js)
-# keep the Places version of the history sidebar at history/history-panel.xul
-# to prevent having to worry about between versions of the browser
-* content/browser/history/history-panel.xul (content/history-panel.xul)
- content/browser/places/history-panel.js (content/history-panel.js)
-# ditto for the bookmarks sidebar
- content/browser/bookmarks/bookmarksPanel.xul (content/bookmarksPanel.xul)
- content/browser/bookmarks/bookmarksPanel.js (content/bookmarksPanel.js)
-* content/browser/bookmarks/sidebarUtils.js (content/sidebarUtils.js)
- content/browser/places/moveBookmarks.xul (content/moveBookmarks.xul)
- content/browser/places/moveBookmarks.js (content/moveBookmarks.js)
- content/browser/places/editBookmarkOverlay.xul (content/editBookmarkOverlay.xul)
- content/browser/places/editBookmarkOverlay.js (content/editBookmarkOverlay.js)
-* content/browser/places/downloadsViewOverlay.xul (content/downloadsViewOverlay.xul)
diff --git a/components/places/moz.build b/components/places/moz.build
deleted file mode 100644
index f8b0d12..0000000
--- a/components/places/moz.build
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-JAR_MANIFESTS += ['jar.mn']
-
-EXTRA_JS_MODULES += [ 'PlacesUIUtils.jsm' ]