gecko/browser/components/places/content/utils.js

1334 lines
45 KiB
JavaScript

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Places Command Controller.
*
* The Initial Developer of the Original Code is Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <beng@google.com>
* Myk Melez <myk@mozilla.org>
* Asaf Romano <mano@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
function LOG(str) {
dump("*** " + str + "\n");
}
var Ci = Components.interfaces;
var Cc = Components.classes;
var Cr = Components.results;
const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
const DESCRIPTION_ANNO = "bookmarkProperties/description";
const POST_DATA_ANNO = "URIProperties/POSTData";
function QI_node(aNode, aIID) {
var result = null;
try {
result = aNode.QueryInterface(aIID);
}
catch (e) {
}
NS_ASSERT(result, "Node QI Failed");
return result;
}
function asVisit(aNode) { return QI_node(aNode, Ci.nsINavHistoryVisitResultNode); }
function asFullVisit(aNode){ return QI_node(aNode, Ci.nsINavHistoryFullVisitResultNode);}
function asContainer(aNode){ return QI_node(aNode, Ci.nsINavHistoryContainerResultNode);}
function asQuery(aNode) { return QI_node(aNode, Ci.nsINavHistoryQueryResultNode); }
var PlacesUtils = {
// Place entries that are containers, e.g. bookmark folders or queries.
TYPE_X_MOZ_PLACE_CONTAINER: "text/x-moz-place-container",
// Place entries that are bookmark separators.
TYPE_X_MOZ_PLACE_SEPARATOR: "text/x-moz-place-separator",
// Place entries that are not containers or separators
TYPE_X_MOZ_PLACE: "text/x-moz-place",
// Place entries in shortcut url format (url\ntitle)
TYPE_X_MOZ_URL: "text/x-moz-url",
// Place entries formatted as HTML anchors
TYPE_HTML: "text/html",
// Place entries as raw URL text
TYPE_UNICODE: "text/unicode",
/**
* The Bookmarks Service.
*/
_bookmarks: null,
get bookmarks() {
if (!this._bookmarks) {
this._bookmarks = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
}
return this._bookmarks;
},
/**
* The Nav History Service.
*/
_history: null,
get history() {
if (!this._history) {
this._history = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
}
return this._history;
},
/**
* The Live Bookmark Service.
*/
_livemarks: null,
get livemarks() {
if (!this._livemarks) {
this._livemarks = Cc["@mozilla.org/browser/livemark-service;2"].
getService(Ci.nsILivemarkService);
}
return this._livemarks;
},
/**
* The Annotations Service.
*/
_annotations: null,
get annotations() {
if (!this._annotations) {
this._annotations = Cc["@mozilla.org/browser/annotation-service;1"].
getService(Ci.nsIAnnotationService);
}
return this._annotations;
},
/**
* The Favicons Service
*/
_favicons: null,
get favicons() {
if (!this._favicons) {
this._favicons = Cc["@mozilla.org/browser/favicon-service;1"].
getService(Ci.nsIFaviconService);
}
return this._favicons;
},
/**
* The Microsummary Service
*/
_microsummaries: null,
get microsummaries() {
if (!this._microsummaries)
this._microsummaries = Cc["@mozilla.org/microsummary/service;1"].
getService(Ci.nsIMicrosummaryService);
return this._microsummaries;
},
_RDF: null,
get RDF() {
if (!this._RDF)
this._RDF = Cc["@mozilla.org/rdf/rdf-service;1"].
getService(Ci.nsIRDFService);
return this._RDF;
},
_localStore: null,
get localStore() {
if (!this._localStore)
this._localStore = this.RDF.GetDataSource("rdf:local-store");
return this._localStore;
},
/**
* The Transaction Manager for this window.
*/
_tm: null,
get tm() {
if (!this._tm) {
this._tm = Cc["@mozilla.org/transactionmanager;1"].
createInstance(Ci.nsITransactionManager);
}
return this._tm;
},
/**
* Makes a URI from a spec.
* @param aSpec
* The string spec of the URI
* @returns A URI object for the spec.
*/
_uri: function PU__uri(aSpec) {
NS_ASSERT(aSpec, "empty URL spec");
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(aSpec, null, null);
},
/**
* Wraps a string in a nsISupportsString wrapper
* @param aString
* The string to wrap
* @returns A nsISupportsString object containing a string.
*/
_wrapString: function PU__wrapString(aString) {
var s = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
s.data = aString;
return s;
},
/**
* String bundle helpers
*/
__bundle: null,
get _bundle() {
if (!this.__bundle) {
const PLACES_STRING_BUNDLE_URI =
"chrome://browser/locale/places/places.properties";
this.__bundle = Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService).
createBundle(PLACES_STRING_BUNDLE_URI);
}
return this.__bundle;
},
getFormattedString: function PU_getFormattedString(key, params) {
return this._bundle.formatStringFromName(key, params, params.length);
},
getString: function PU_getString(key) {
return this._bundle.GetStringFromName(key);
},
/**
* Determines whether or not a ResultNode is a Bookmark folder or not.
* @param aNode
* A NavHistoryResultNode
* @returns true if the node is a Bookmark folder, false otherwise
*/
nodeIsFolder: function PU_nodeIsFolder(aNode) {
NS_ASSERT(aNode, "null node");
return (aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER);
},
/**
* Determines whether or not a ResultNode represents a bookmarked URI.
* @param aNode
* A NavHistoryResultNode
* @returns true if the node represents a bookmarked URI, false otherwise
*/
nodeIsBookmark: function PU_nodeIsBookmark(aNode) {
NS_ASSERT(aNode, "null node");
return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_URI &&
aNode.itemId != -1;
},
/**
* Determines whether or not a ResultNode is a Bookmark separator.
* @param aNode
* A NavHistoryResultNode
* @returns true if the node is a Bookmark separator, false otherwise
*/
nodeIsSeparator: function PU_nodeIsSeparator(aNode) {
NS_ASSERT(aNode, "null node");
return (aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR);
},
/**
* Determines whether or not a ResultNode is a visit item or not
* @param aNode
* A NavHistoryResultNode
* @returns true if the node is a visit item, false otherwise
*/
nodeIsVisit: function PU_nodeIsVisit(aNode) {
NS_ASSERT(aNode, "null node");
const NHRN = Ci.nsINavHistoryResultNode;
var type = aNode.type;
return type == NHRN.RESULT_TYPE_VISIT ||
type == NHRN.RESULT_TYPE_FULL_VISIT;
},
/**
* Determines whether or not a ResultNode is a URL item or not
* @param aNode
* A NavHistoryResultNode
* @returns true if the node is a URL item, false otherwise
*/
nodeIsURI: function PU_nodeIsURI(aNode) {
NS_ASSERT(aNode, "null node");
const NHRN = Ci.nsINavHistoryResultNode;
var type = aNode.type;
return type == NHRN.RESULT_TYPE_URI ||
type == NHRN.RESULT_TYPE_VISIT ||
type == NHRN.RESULT_TYPE_FULL_VISIT;
},
/**
* Determines whether or not a ResultNode is a Query item or not
* @param aNode
* A NavHistoryResultNode
* @returns true if the node is a Query item, false otherwise
*/
nodeIsQuery: function PU_nodeIsQuery(aNode) {
NS_ASSERT(aNode, "null node");
return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY;
},
/**
* Determines if a node is read only (children cannot be inserted, sometimes
* they cannot be removed depending on the circumstance)
* @param aNode
* A NavHistoryResultNode
* @returns true if the node is readonly, false otherwise
*/
nodeIsReadOnly: function PU_nodeIsReadOnly(aNode) {
NS_ASSERT(aNode, "null node");
if (this.nodeIsFolder(aNode))
return this.bookmarks.getFolderReadonly(aNode.itemId);
if (this.nodeIsQuery(aNode))
return asQuery(aNode).childrenReadOnly;
return false;
},
/**
* Determines whether or not a ResultNode is a host folder or not
* @param aNode
* A NavHistoryResultNode
* @returns true if the node is a host item, false otherwise
*/
nodeIsHost: function PU_nodeIsHost(aNode) {
NS_ASSERT(aNode, "null node");
return aNode.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_HOST;
},
/**
* Determines whether or not a ResultNode is a container item or not
* @param aNode
* A NavHistoryResultNode
* @returns true if the node is a container item, false otherwise
*/
nodeIsContainer: function PU_nodeIsContainer(aNode) {
NS_ASSERT(aNode, "null node");
const NHRN = Ci.nsINavHistoryResultNode;
var type = aNode.type;
return type == NHRN.RESULT_TYPE_HOST ||
type == NHRN.RESULT_TYPE_QUERY ||
type == NHRN.RESULT_TYPE_FOLDER ||
type == NHRN.RESULT_TYPE_DAY ||
type == NHRN.RESULT_TYPE_REMOTE_CONTAINER;
},
/**
* Determines whether or not a ResultNode is a remotecontainer item.
* ResultNote may be either a remote container result type or a bookmark folder
* with a nonempty remoteContainerType. The remote container result node
* type is for dynamically created remote containers (i.e., for the file
* browser service where you get your folders in bookmark menus). Bookmark
* folders are marked as remote containers when some other component is
* registered as interested in them and providing some operations, in which
* case their remoteContainerType indicates which component is thus registered.
* For exmaple, the livemark service uses this mechanism.
* @param aNode
* A NavHistoryResultNode
* @returns true if the node is a container item, false otherwise
*/
nodeIsRemoteContainer: function PU_nodeIsRemoteContainer(aNode) {
NS_ASSERT(aNode, "null node");
const NHRN = Ci.nsINavHistoryResultNode;
if (aNode.type == NHRN.RESULT_TYPE_REMOTE_CONTAINER)
return true;
if (this.nodeIsFolder(aNode))
return asContainer(aNode).remoteContainerType != "";
return false;
},
/**
* Determines whether a ResultNode is a remote container registered by the
* livemark service.
* @param aNode
* A NavHistory Result Node
* @returns true if the node is a livemark container item
*/
nodeIsLivemarkContainer: function PU_nodeIsLivemarkContainer(aNode) {
return (this.nodeIsRemoteContainer(aNode) &&
asContainer(aNode).remoteContainerType ==
"@mozilla.org/browser/livemark-service;2");
},
/**
* Determines whether a ResultNode is a live-bookmark item
* @param aNode
* A NavHistory Result Node
* @returns true if the node is a livemark container item
*/
nodeIsLivemarkItem: function PU_nodeIsLivemarkItem(aNode) {
if (this.nodeIsBookmark(aNode)) {
if (this.annotations
.itemHasAnnotation(aNode.itemId, "livemark/bookmarkFeedURI"))
return true;
}
return false;
},
/**
* Determines whether or not a node is a readonly folder.
* @param aNode
* The node to test.
* @returns true if the node is a readonly folder.
*/
isReadonlyFolder: function(aNode) {
NS_ASSERT(aNode, "null node");
return this.nodeIsFolder(aNode) &&
this.bookmarks.getFolderReadonly(aNode.itemId);
},
/**
* Gets the index of a node within its parent container
* @param aNode
* The node to look up
* @returns The index of the node within its parent container, or -1 if the
* node was not found or the node specified has no parent.
*/
getIndexOfNode: function PU_getIndexOfNode(aNode) {
NS_ASSERT(aNode, "null node");
var parent = aNode.parent;
if (!parent || !PlacesUtils.nodeIsContainer(parent))
return -1;
var wasOpen = parent.containerOpen;
parent.containerOpen = true;
var cc = parent.childCount;
asContainer(parent);
for (var i = 0; i < cc && parent.getChild(i) != aNode; ++i);
parent.containerOpen = wasOpen;
return i < cc ? i : -1;
},
/**
* String-wraps a NavHistoryResultNode according to the rules of the specified
* content type.
* @param aNode
* The Result node to wrap (serialize)
* @param aType
* The content type to serialize as
* @param [optional] aOverrideURI
* Used instead of the node's URI if provided.
* This is useful for wrapping a container as TYPE_X_MOZ_URL,
* TYPE_HTML or TYPE_UNICODE.
* @returns A string serialization of the node
*/
wrapNode: function PU_wrapNode(aNode, aType, aOverrideURI) {
switch (aType) {
case this.TYPE_X_MOZ_PLACE_CONTAINER:
case this.TYPE_X_MOZ_PLACE:
case this.TYPE_X_MOZ_PLACE_SEPARATOR:
// Data is encoded like this:
// bookmarks folder: <itemId>\n<>\n<parentId>\n<indexInParent>
// uri: 0\n<uri>\n<parentId>\n<indexInParent>
// bookmark: <itemId>\n<uri>\n<parentId>\n<indexInParent>
// separator: <itemId>\n<>\n<parentId>\n<indexInParent>
var wrapped = "";
if (aNode.itemId != -1) //
wrapped += aNode.itemId + NEWLINE;
else
wrapped += "0" + NEWLINE;
if (this.nodeIsURI(aNode) || this.nodeIsQuery(aNode))
wrapped += aNode.uri + NEWLINE;
else
wrapped += NEWLINE;
if (this.nodeIsFolder(aNode.parent))
wrapped += aNode.parent.itemId + NEWLINE;
else
wrapped += "0" + NEWLINE;
wrapped += this.getIndexOfNode(aNode);
return wrapped;
case this.TYPE_X_MOZ_URL:
return (aOverrideURI || aNode.uri) + NEWLINE + aNode.title;
case this.TYPE_HTML:
return "<A HREF=\"" + (aOverrideURI || aNode.uri) + "\">" +
aNode.title + "</A>";
}
// case this.TYPE_UNICODE:
return (aOverrideURI || aNode.uri);
},
/**
* Get a transaction for copying a uri item from one container to another
* as a bookmark.
* @param aURI
* The URI of the item being copied
* @param aContainer
* The container being copied into
* @param aIndex
* The index within the container the item is copied to
* @returns A nsITransaction object that performs the copy.
*/
_getURIItemCopyTransaction: function (aURI, aContainer, aIndex) {
var title = this.history.getPageTitle(aURI);
return new PlacesCreateItemTransaction(aURI, aContainer, aIndex, title);
},
/**
* Get a transaction for copying a bookmark item from one container to
* another.
* @param aID
* The identifier of the bookmark item being copied
* @param aContainer
* The container being copied into
* @param aIndex
* The index within the container the item is copied to
* @param [optional] aExcludeAnnotations
* Optional, array of annotations (listed by their names) to exclude
* when copying the item.
* @returns A nsITransaction object that performs the copy.
*/
_getBookmarkItemCopyTransaction:
function PU__getBookmarkItemCopyTransaction(aId, aContainer, aIndex,
aExcludeAnnotations) {
var bookmarks = this.bookmarks;
var itemURL = bookmarks.getBookmarkURI(aId);
var itemTitle = bookmarks.getItemTitle(aId);
var keyword = bookmarks.getKeywordForBookmark(aId);
var annos = this.getAnnotationsForItem(aId);
if (aExcludeAnnotations) {
annos =
annos.filter(function(aValue, aIndex, aArray) {
return aExcludeAnnotations.indexOf(aValue.name) == -1;
});
}
var createTxn =
new PlacesCreateItemTransaction(itemURL, aContainer, aIndex, itemTitle,
keyword, annos);
return createTxn;
},
/**
* Gets a transaction for copying (recursively nesting to include children)
* a folder and its contents from one folder to another.
* @param aData
* Unwrapped dropped folder data
* @param aContainer
* The container we are copying into
* @param aIndex
* The index in the destination container to insert the new items
* @returns A nsITransaction object that will perform the copy.
*/
_getFolderCopyTransaction:
function PU__getFolderCopyTransaction(aData, aContainer, aIndex) {
var self = this;
function getChildItemsTransactions(aFolderId) {
var childItemsTransactions = [];
var children = self.getFolderContents(aFolderId, false, false);
var cc = children.childCount;
for (var i = 0; i < cc; ++i) {
var txn = null;
var node = children.getChild(i);
if (self.nodeIsFolder(node)) {
var nodeFolderId = node.itemId;
var title = self.bookmarks.getItemTitle(nodeFolderId);
var annos = self.getAnnotationsForItem(nodeFolderId);
var folderItemsTransactions =
getChildItemsTransactions(nodeFolderId);
txn = new PlacesCreateFolderTransaction(title, -1, aIndex, annos,
folderItemsTransactions);
}
else if (self.nodeIsBookmark(node)) {
txn = self._getBookmarkItemCopyTransaction(node.itemId, -1,
aIndex);
}
else if (self.nodeIsURI(node) || self.nodeIsQuery(node)) {
// XXXmano: can this ^ ever happen?
txn = self._getURIItemCopyTransaction(self._uri(node.uri), -1,
aIndex);
}
else if (self.nodeIsSeparator(node))
txn = new PlacesCreateSeparatorTransaction(-1, aIndex);
NS_ASSERT(txn, "Unexpected item under a bookmarks folder");
if (txn)
childItemsTransactions.push(txn);
}
return childItemsTransactions;
}
var title = this.bookmarks.getItemTitle(aData.id);
var annos = this.getAnnotationsForItem(aData.id);
var createTxn =
new PlacesCreateFolderTransaction(title, aContainer, aIndex, annos,
getChildItemsTransactions(aData.id));
return createTxn;
},
/**
* Unwraps data from the Clipboard or the current Drag Session.
* @param blob
* A blob (string) of data, in some format we potentially know how
* to parse.
* @param type
* The content type of the blob.
* @returns An array of objects representing each item contained by the source.
*/
unwrapNodes: function PU_unwrapNodes(blob, type) {
// We use \n here because the transferable system converts \r\n to \n
var parts = blob.split("\n");
var nodes = [];
for (var i = 0; i < parts.length; ++i) {
var data = { };
switch (type) {
case this.TYPE_X_MOZ_PLACE_CONTAINER:
case this.TYPE_X_MOZ_PLACE:
case this.TYPE_X_MOZ_PLACE_SEPARATOR:
// Data in these types has 4 parts, so if there are less than 4 parts
// remaining, the data blob is malformed and we should stop.
if (i > (parts.length - 4))
break;
nodes.push({ id: parseInt(parts[i++]),
uri: parts[i] ? this._uri(parts[i]) : null,
parent: parseInt(parts[++i]),
index: parseInt(parts[++i]) });
break;
case this.TYPE_X_MOZ_URL:
// See above.
if (i > (parts.length - 2))
break;
nodes.push({ uri: this._uri(parts[i++]),
title: parts[i] ? parts[i] : parts[i-1] });
break;
case this.TYPE_UNICODE:
// See above.
if (i > (parts.length - 1))
break;
nodes.push({ uri: this._uri(parts[i]) });
break;
default:
LOG("Cannot unwrap data of type " + type);
throw Cr.NS_ERROR_INVALID_ARG;
}
}
return nodes;
},
/**
* 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 PU_makeTransaction(data, type, container,
index, copy) {
switch (type) {
case this.TYPE_X_MOZ_PLACE_CONTAINER:
if (data.id > 0) {
// Place is a folder.
if (copy)
return this._getFolderCopyTransaction(data, container, index);
}
break;
case this.TYPE_X_MOZ_PLACE:
if (data.id <= 0)
return this._getURIItemCopyTransaction(data.uri, container, index);
if (copy) {
// Copying a child of a live-bookmark by itself should result
// as a new normal bookmark item (bug 376731)
var copyBookmarkAnno =
this._getBookmarkItemCopyTransaction(data.id, container, index,
["livemark/bookmarkFeedURI"]);
return copyBookmarkAnno;
}
break;
case this.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);
}
break;
case this.TYPE_X_MOZ_URL:
case this.TYPE_UNICODE:
var title = type == this.TYPE_X_MOZ_URL ? data.title : data.uri.spec;
var createTxn =
new PlacesCreateItemTransaction(data.uri, container, index, title);
return createTxn;
default:
return null;
}
if (data.id <= 0)
return null;
// Move the item otherwise
return new PlacesMoveItemTransaction(data.id, container, index);
},
/**
* Generates a nsINavHistoryResult for the contents of a folder.
* @param folderId
* The folder to open
* @param [optional] excludeItems
* True to hide all items (individual bookmarks). This is used on
* the left places pane so you just get a folder hierarchy.
* @param [optional] expandQueries
* True to make query items expand as new containers. For managing,
* you want this to be false, for menus and such, you want this to
* be true.
* @returns A nsINavHistoryResult containing the contents of the
* folder. The result.root is guaranteed to be open.
*/
getFolderContents:
function PU_getFolderContents(aFolderId, aExcludeItems, aExpandQueries) {
var query = this.history.getNewQuery();
query.setFolders([aFolderId], 1);
var options = this.history.getNewQueryOptions();
options.setGroupingMode([Ci.nsINavHistoryQueryOptions.GROUP_BY_FOLDER], 1);
options.excludeItems = aExcludeItems;
options.expandQueries = aExpandQueries;
var result = this.history.executeQuery(query, options);
result.root.containerOpen = true;
asContainer(result.root);
return result;
},
/**
* Methods to show the bookmarkProperties dialog in its various modes.
*
* The showMinimalAdd* methods open the dialog by its alternative URI. Thus
* they persist the dialog dimensions separately from the showAdd* methods.
* Note these variants also do not return the dialog "performed" state since
* they may not open the dialog modally.
*/
/**
* Shows the "Add Bookmark" dialog.
*
* @param [optional] aURI
* An nsIURI object for which the "add bookmark" dialog is
* to be shown.
* @param [optional] aTitle
* The default title for the new bookmark.
* @param [optional] aDescription
The default description for the new bookmark
* @param [optional] aDefaultInsertionPoint
* The default insertion point for the new item. If set, the folder
* picker would be hidden unless aShowPicker is set to true, in which
* case the dialog only uses the folder identifier from the insertion
* point as the initially selected item in the folder picker.
* @param [optional] aShowPicker
* see above
* @param [optional] aLoadInSidebar
* If true, the dialog will default to load the new item in the
* sidebar (as a web panel).
* @param [optional] aKeyword
* The default keyword for the new bookmark. The keyword field
* will be shown in the dialog if this is used.
* @param [optional] aPostData
* POST data for POST-style keywords.
* @return true if any transaction has been performed.
*
* Notes:
* - the location, description and "load in sidebar" fields are
* visible only if there is no initial URI (aURI is null).
* - When aDefaultInsertionPoint is not set, the dialog defaults to the
* bookmarks root folder.
*/
showAddBookmarkUI: function PU_showAddBookmarkUI(aURI,
aTitle,
aDescription,
aDefaultInsertionPoint,
aShowPicker,
aLoadInSidebar,
aKeyword,
aPostData) {
var info = {
action: "add",
type: "bookmark"
};
if (aURI)
info.uri = aURI;
// allow default empty title
if (typeof(aTitle) == "string")
info.title = aTitle;
if (aDescription)
info.description = aDescription;
if (aDefaultInsertionPoint) {
info.defaultInsertionPoint = aDefaultInsertionPoint;
if (!aShowPicker)
info.hiddenRows = ["folder picker"];
}
if (aLoadInSidebar)
info.loadBookmarkInSidebar = true;
if (typeof(aKeyword) == "string") {
info.keyword = aKeyword;
if (typeof(aPostData) == "string")
info.postData = aPostData;
}
return this._showBookmarkDialog(info);
},
/**
* @see showAddBookmarkUI
* This opens the dialog with only the name and folder pickers visible by
* default.
*
* You can still pass in the various paramaters as the default properties
* for the new bookmark.
*
* The keyword field will be visible only if the aKeyword parameter
* was used.
*/
showMinimalAddBookmarkUI:
function PU_showMinimalAddBookmarkUI(aURI, aTitle, aDescription,
aDefaultInsertionPoint, aShowPicker,
aLoadInSidebar, aKeyword, aPostData) {
var info = {
action: "add",
type: "bookmark",
hiddenRows: ["location", "description", "load in sidebar"]
};
if (aURI)
info.uri = aURI;
// allow default empty title
if (typeof(aTitle) == "string")
info.title = aTitle;
if (aDescription)
info.description = aDescription;
if (aDefaultInsertionPoint) {
info.defaultInsertionPoint = aDefaultInsertionPoint;
if (!aShowPicker)
info.hiddenRows.push("folder picker");
}
if (aLoadInSidebar)
info.loadBookmarkInSidebar = true;
if (typeof(aKeyword) == "string") {
info.keyword = aKeyword;
if (typeof(aPostData) == "string")
info.postData = aPostData;
}
else
info.hiddenRows.push("keyword");
this._showBookmarkDialog(info, true);
},
/**
* Shows the "Add Live Bookmark" dialog.
*
* @param [optional] aFeedURI
* The feed URI for which the dialog is to be shown (nsIURI).
* @param [optional] aSiteURI
* The site URI for the new live-bookmark (nsIURI).
* @param [optional] aDefaultInsertionPoint
* The default insertion point for the new item. If set, the folder
* picker would be hidden unless aShowPicker is set to true, in which
* case the dialog only uses the folder identifier from the insertion
* point as the initially selected item in the folder picker.
* @param [optional] aShowPicker
* see above
* @return true if any transaction has been performed.
*
* Notes:
* - the feedURI and description fields are visible only if there is no
* initial feed URI (aFeedURI is null).
* - When aDefaultInsertionPoint is not set, the dialog defaults to the
* bookmarks root folder.
*/
showAddLivemarkUI: function PU_showAddLivemarkURI(aFeedURI,
aSiteURI,
aTitle,
aDescription,
aDefaultInsertionPoint,
aShowPicker) {
var info = {
action: "add",
type: "livemark"
};
if (aFeedURI)
info.feedURI = aFeedURI;
if (aSiteURI)
info.siteURI = aSiteURI;
// allow default empty title
if (typeof(aTitle) == "string")
info.title = aTitle;
if (aDescription)
info.description = aDescription;
if (aDefaultInsertionPoint) {
info.defaultInsertionPoint = aDefaultInsertionPoint;
if (!aShowPicker)
info.hiddenRows = ["folder picker"];
}
return this._showBookmarkDialog(info);
},
/**
* @see showAddLivemarkUI
* This opens the dialog with only the name and folder pickers visible by
* default.
*
* You can still pass in the various paramaters as the default properties
* for the new live-bookmark.
*/
showMinimalAddLivemarkUI:
function PU_showMinimalAddLivemarkURI(aFeedURI, aSiteURI, aTitle,
aDescription, aDefaultInsertionPoint,
aShowPicker) {
var info = {
action: "add",
type: "livemark",
hiddenRows: ["feedURI", "siteURI", "description"]
};
if (aFeedURI)
info.feedURI = aFeedURI;
if (aSiteURI)
info.siteURI = aSiteURI;
// allow default empty title
if (typeof(aTitle) == "string")
info.title = aTitle;
if (aDescription)
info.description = aDescription;
if (aDefaultInsertionPoint) {
info.defaultInsertionPoint = aDefaultInsertionPoint;
if (!aShowPicker)
info.hiddenRows.push("folder picker");
}
this._showBookmarkDialog(info, true);
},
/**
* Show an "Add Bookmarks" dialog to allow the adding of a folder full
* of bookmarks corresponding to the objects in the uriList. This will
* be called most often as the result of a "Bookmark All Tabs..." command.
*
* @param aURIList List of nsIURI objects representing the locations
* to be bookmarked.
* @return true if any transaction has been performed.
*/
showMinimalAddMultiBookmarkUI: function PU_showAddMultiBookmarkUI(aURIList) {
NS_ASSERT(aURIList.length,
"showAddMultiBookmarkUI expects a list of nsIURI objects");
var info = {
action: "add",
type: "folder",
hiddenRows: ["description"],
URIList: aURIList
};
this._showBookmarkDialog(info, true);
},
/**
* Opens the bookmark properties panel for a given bookmark identifier.
*
* @param aId
* bookmark identifier for which the properties are to be shown
* @return true if any transaction has been performed.
*/
showBookmarkProperties: function PU_showBookmarkProperties(aId) {
var info = {
action: "edit",
type: "bookmark",
bookmarkId: aId
};
return this._showBookmarkDialog(info);
},
/**
* Opens the folder properties panel for a given folder ID.
*
* @param aId
* an integer representing the ID of the folder to edit
* @return true if any transaction has been performed.
*/
showFolderProperties: function PU_showFolderProperties(aId) {
var info = {
action: "edit",
type: "folder",
folderId: aId
};
return this._showBookmarkDialog(info);
},
/**
* Shows the "New Folder" dialog.
*
* @param [optional] aTitle
* The default title for the new bookmark.
* @param [optional] aDefaultInsertionPoint
* The default insertion point for the new item. If set, the folder
* picker would be hidden unless aShowPicker is set to true, in which
* case the dialog only uses the folder identifier from the insertion
* point as the initially selected item in the folder picker.
* @param [optional] aShowPicker
* see above
* @return true if any transaction has been performed.
*/
showAddFolderUI:
function PU_showAddFolderUI(aTitle, aDefaultInsertionPoint, aShowPicker) {
var info = {
action: "add",
type: "folder",
hiddenRows: []
};
// allow default empty title
if (typeof(aTitle) == "string")
info.title = aTitle;
if (aDefaultInsertionPoint) {
info.defaultInsertionPoint = aDefaultInsertionPoint;
if (!aShowPicker)
info.hiddenRows.push("folder picker");
}
return this._showBookmarkDialog(info);
},
/**
* 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 aMinimalUI
* [optional] if true, the dialog is opened by its alternative
* chrome: uri.
*
* Note: In minimal UI mode, we open the dialog non-modal on any system but
* Mac OS X.
* @return true if any transaction has been performed, false otherwise.
* Note: the return value of this method is not reliable in minimal UI mode
* since the dialog may not be opened modally.
*/
_showBookmarkDialog: function PU__showBookmarkDialog(aInfo, aMinimalUI) {
var dialogURL = aMinimalUI ?
"chrome://browser/content/places/bookmarkProperties2.xul" :
"chrome://browser/content/places/bookmarkProperties.xul";
var features;
if (aMinimalUI)
#ifdef XP_MACOSX
features = "centerscreen,chrome,dialog,resizable,modal";
#else
features = "centerscreen,chrome,dialog,resizable,dependent";
#endif
else
features = "centerscreen,chrome,modal,resizable=no";
window.openDialog(dialogURL, "", features, aInfo);
return ("performed" in aInfo && aInfo.performed);
},
/**
* 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(aNode) {
var node = aNode;
while (node) {
// XXXmano: Use QueryInterface(nsIPlacesView) once we implement it...
if (node.getAttribute("type") == "places")
return node;
node = node.parentNode;
}
return null;
},
/**
* Allows opening of javascript/data URI only if the given node is
* bookmarked (see bug 224521).
* @param aURINode
* a URI node
* @return true if it's safe to open the node in the browser, false otherwise.
*
*/
checkURLSecurity: function PU_checkURLSecurity(aURINode) {
if (!this.nodeIsBookmark(aURINode)) {
var uri = this._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 promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
getService(Ci.nsIPromptService);
var errorStr = this.getString("load-js-data-url-error");
promptService.alert(window, brandShortName, errorStr);
return false;
}
}
return true;
},
/**
* Fetch all annotations for a URI, including all properties of each
* annotation which would be required to recreate it.
* @param aURI
* The URI for which annotations are to be retrieved.
* @return Array of objects, each containing the following properties:
* name, flags, expires, mimeType, type, value
*/
getAnnotationsForURI: function PU_getAnnotationsForURI(aURI) {
var annosvc = this.annotations;
var annos = [], val = null;
var annoNames = annosvc.getPageAnnotationNames(aURI, {});
for (var i = 0; i < annoNames.length; i++) {
var flags = {}, exp = {}, mimeType = {}, storageType = {};
annosvc.getPageAnnotationInfo(aURI, annoNames[i], flags, exp, mimeType, storageType);
if (storageType.value == annosvc.TYPE_BINARY) {
var data = {}, length = {}, mimeType = {};
annosvc.getPageAnnotationBinary(aURI, annoNames[i], data, length, mimeType);
val = data.value;
}
else
val = annosvc.getPageAnnotation(aURI, annoNames[i]);
annos.push({name: annoNames[i],
flags: flags.value,
expires: exp.value,
mimeType: mimeType.value,
type: storageType.value,
value: val});
}
return annos;
},
/**
* Fetch all annotations for an item, including all properties of each
* annotation which would be required to recreate it.
* @param aItemId
* The identifier of the itme for which annotations are to be
* retrieved.
* @return Array of objects, each containing the following properties:
* name, flags, expires, mimeType, type, value
*/
getAnnotationsForItem: function PU_getAnnotationsForItem(aItemId) {
var annosvc = this.annotations;
var annos = [], val = null;
var annoNames = annosvc.getItemAnnotationNames(aItemId, {});
for (var i = 0; i < annoNames.length; i++) {
var flags = {}, exp = {}, mimeType = {}, storageType = {};
annosvc.getItemAnnotationInfo(aItemId, annoNames[i], flags, exp, mimeType, storageType);
if (storageType.value == annosvc.TYPE_BINARY) {
var data = {}, length = {}, mimeType = {};
annosvc.geItemAnnotationBinary(aItemId, annoNames[i], data, length, mimeType);
val = data.value;
}
else
val = annosvc.getItemAnnotation(aItemId, annoNames[i]);
annos.push({name: annoNames[i],
flags: flags.value,
expires: exp.value,
mimeType: mimeType.value,
type: storageType.value,
value: val});
}
return annos;
},
/**
* Annotate a URI with a batch of annotations.
* @param aURI
* The URI for which annotations are to be set.
* @param aAnnotations
* Array of objects, each containing the following properties:
* name, flags, expires, type, mimeType (only used for binary
* annotations) value.
*/
setAnnotationsForURI: function PU_setAnnotationsForURI(aURI, aAnnos) {
var annosvc = this.annotations;
aAnnos.forEach(function(anno) {
if (anno.type == annosvc.TYPE_BINARY) {
annosvc.setPageAnnotationBinary(aURI, anno.name, anno.value,
anno.value.length, anno.mimeType,
anno.flags, anno.expires);
}
else {
annosvc.setPageAnnotation(aURI, anno.name, anno.value,
anno.flags, anno.expires);
}
});
},
/**
* Annotate an item with a batch of annotations.
* @param aItemId
* The identifier of the item for which annotations are to be set
* @param aAnnotations
* Array of objects, each containing the following properties:
* name, flags, expires, type, mimeType (only used for binary
* annotations) value.
*/
setAnnotationsForItem: function PU_setAnnotationsForItem(aItemId, aAnnos) {
var annosvc = this.annotations;
aAnnos.forEach(function(anno) {
if (anno.type == annosvc.TYPE_BINARY) {
annosvc.setItemAnnotationBinary(aItemId, anno.name, anno.value,
anno.value.length, anno.mimeType,
anno.flags, anno.expires);
}
else {
annosvc.setItemAnnotation(aItemId, anno.name, anno.value,
anno.flags, anno.expires);
}
});
},
/**
* Helper for getting a serialized Places query for a particular folder.
* @param aFolderId The folder id to get a query for.
* @return string serialized place URI
*/
getQueryStringForFolder: function PU_getQueryStringForFolder(aFolderId) {
var options = this.history.getNewQueryOptions();
options.setGroupingMode([Ci.nsINavHistoryQueryOptions.GROUP_BY_FOLDER], 1);
var query = this.history.getNewQuery();
query.setFolders([aFolderId], 1);
return this.history.queriesToQueryString([query], 1, options);
},
/**
* 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 PU_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 "";
},
// identifier getters for special folders
get placesRootId() {
if (!("_placesRootId" in this))
this._placesRootId = this.bookmarks.placesRoot;
return this._placesRootId;
},
get bookmarksRootId() {
if (!("_bookmarksRootId" in this))
this._bookmarksRootId = this.bookmarks.bookmarksRoot;
return this._bookmarksRootId;
},
get toolbarFolderId() {
if (!("_toolbarFolderId" in this))
this._toolbarFolderId = this.bookmarks.toolbarFolder;
return this._toolbarFolderId;
},
/**
* Set the POST data associated with a URI, if any.
* Used by POST keywords.
* @param aURI
* @returns string of POST data
*/
setPostDataForURI: function PU_setPostDataForURI(aURI, aPostData) {
const annos = this.annotations;
if (aPostData)
annos.setPageAnnotation(aURI, POST_DATA_ANNO, aPostData, 0, 0);
else if (annos.pageHasAnnotation(aURI, POST_DATA_ANNO))
annos.removePageAnnotation(aURI, POST_DATA_ANNO);
},
/**
* Get the POST data associated with a bookmark, if any.
* @param aURI
* @returns string of POST data if set for aURI. null otherwise.
*/
getPostDataForURI: function PU_getPostDataForURI(aURI) {
const annos = this.annotations;
if (annos.pageHasAnnotation(aURI, POST_DATA_ANNO))
return annos.getPageAnnotation(aURI, POST_DATA_ANNO);
return null;
}
};
PlacesUtils.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,
PlacesUtils.TYPE_UNICODE];