зеркало из https://github.com/mozilla/gecko-dev.git
Bug 416459 - Implement proper cut action for bookmarks.
r=dietrich
This commit is contained in:
Родитель
986b94d168
Коммит
309f2be51a
|
@ -431,6 +431,19 @@ PlacesViewBase.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
toggleCutNode: function PVB_toggleCutNode(aNode, aValue) {
|
||||
let elt = aNode._DOMElement;
|
||||
if (elt) {
|
||||
// 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 = aPlacesNode._DOMElement;
|
||||
if (!elt)
|
||||
|
@ -658,6 +671,7 @@ PlacesViewBase.prototype = {
|
|||
}
|
||||
|
||||
if (this._controller) {
|
||||
this._controller.terminate();
|
||||
this._viewElt.controllers.removeController(this._controller);
|
||||
this._controller = null;
|
||||
}
|
||||
|
|
|
@ -117,6 +117,9 @@ function PlacesController(aView) {
|
|||
XPCOMUtils.defineLazyServiceGetter(this, "clipboard",
|
||||
"@mozilla.org/widget/clipboard;1",
|
||||
"nsIClipboard");
|
||||
XPCOMUtils.defineLazyGetter(this, "profileName", function () {
|
||||
return Services.dirsvc.get("ProfD", Ci.nsIFile).leafName;
|
||||
});
|
||||
}
|
||||
|
||||
PlacesController.prototype = {
|
||||
|
@ -125,6 +128,20 @@ PlacesController.prototype = {
|
|||
*/
|
||||
_view: null,
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIClipboardOwner
|
||||
]),
|
||||
|
||||
// nsIClipboardOwner
|
||||
LosingOwnership: function PC_LosingOwnership (aXferable) {
|
||||
this.cutNodes = [];
|
||||
},
|
||||
|
||||
terminate: function PC_terminate() {
|
||||
if (this._cutNodes.length > 0)
|
||||
this._clearClipboard();
|
||||
},
|
||||
|
||||
supportsCommand: function PC_supportsCommand(aCommand) {
|
||||
//LOG("supportsCommand: " + command);
|
||||
// Non-Places specific commands that we also support
|
||||
|
@ -1086,74 +1103,127 @@ PlacesController.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
get clipboardAction () {
|
||||
let action = {};
|
||||
let actionOwner;
|
||||
try {
|
||||
let xferable = Cc["@mozilla.org/widget/transferable;1"].
|
||||
createInstance(Ci.nsITransferable);
|
||||
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;
|
||||
},
|
||||
|
||||
_clearClipboard: function PC__clearClipboard() {
|
||||
this.clipboard.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
|
||||
// Unfortunately just invoking emptyClipboard is not enough, since it
|
||||
// does not act on the native clipboard.
|
||||
let xferable = Cc["@mozilla.org/widget/transferable;1"].
|
||||
createInstance(Ci.nsITransferable);
|
||||
// GTK doesn't like empty transferables, so just add an unknown type.
|
||||
xferable.addDataFlavor("text/x-moz-place-empty");
|
||||
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 overrideURI = PlacesUtils.nodeIsLivemarkContainer(node) ?
|
||||
PlacesUtils.livemarks.getFeedURI(node.itemId).spec : null;
|
||||
let resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node);
|
||||
|
||||
contents.forEach(function (content) {
|
||||
content.entries.push(
|
||||
PlacesUtils.wrapNode(node, content.type, overrideURI, resolveShortcuts)
|
||||
);
|
||||
});
|
||||
}, 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);
|
||||
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, Ci.nsIClipboard.kGlobalClipboard);
|
||||
},
|
||||
|
||||
_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 {
|
||||
let nodes = this._view.selectedNodes;
|
||||
|
||||
let xferable = Cc["@mozilla.org/widget/transferable;1"].
|
||||
createInstance(Ci.nsITransferable);
|
||||
let foundFolder = false, foundLink = false;
|
||||
let copiedFolders = [];
|
||||
let placeString, mozURLString, htmlString, unicodeString;
|
||||
placeString = mozURLString = htmlString = unicodeString = "";
|
||||
|
||||
for (let i = 0; i < nodes.length; ++i) {
|
||||
let node = nodes[i];
|
||||
if (this._shouldSkipNode(node, copiedFolders))
|
||||
continue;
|
||||
if (PlacesUtils.nodeIsFolder(node))
|
||||
copiedFolders.push(node);
|
||||
|
||||
function generateChunk(type, overrideURI) {
|
||||
let suffix = i < (nodes.length - 1) ? PlacesUtils.endl : "";
|
||||
let uri = overrideURI;
|
||||
|
||||
if (PlacesUtils.nodeIsLivemarkContainer(node))
|
||||
uri = PlacesUtils.livemarks.getFeedURI(node.itemId).spec
|
||||
|
||||
mozURLString += (PlacesUtils.wrapNode(node, PlacesUtils.TYPE_X_MOZ_URL,
|
||||
uri) + suffix);
|
||||
unicodeString += (PlacesUtils.wrapNode(node, PlacesUtils.TYPE_UNICODE,
|
||||
uri) + suffix);
|
||||
htmlString += (PlacesUtils.wrapNode(node, PlacesUtils.TYPE_HTML,
|
||||
uri) + suffix);
|
||||
|
||||
let placeSuffix = i < (nodes.length - 1) ? "," : "";
|
||||
let resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node);
|
||||
return PlacesUtils.wrapNode(node, type, overrideURI, resolveShortcuts) + placeSuffix;
|
||||
}
|
||||
|
||||
// all items wrapped as TYPE_X_MOZ_PLACE
|
||||
placeString += generateChunk(PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
}
|
||||
|
||||
function addData(type, data) {
|
||||
xferable.addDataFlavor(type);
|
||||
xferable.setTransferData(type, PlacesUIUtils._wrapString(data), data.length * 2);
|
||||
}
|
||||
// This order is _important_! It controls how this and other applications
|
||||
// select data to be inserted based on type.
|
||||
if (placeString)
|
||||
addData(PlacesUtils.TYPE_X_MOZ_PLACE, placeString);
|
||||
if (mozURLString)
|
||||
addData(PlacesUtils.TYPE_X_MOZ_URL, mozURLString);
|
||||
if (unicodeString)
|
||||
addData(PlacesUtils.TYPE_UNICODE, unicodeString);
|
||||
if (htmlString)
|
||||
addData(PlacesUtils.TYPE_HTML, htmlString);
|
||||
|
||||
if (placeString || unicodeString || htmlString || mozURLString) {
|
||||
this.clipboard.setData(xferable, null, Ci.nsIClipboard.kGlobalClipboard);
|
||||
}
|
||||
this._populateClipboard(this._view.selectedNodes, "copy");
|
||||
}
|
||||
finally {
|
||||
if (!didSuppressNotifications)
|
||||
|
@ -1165,101 +1235,94 @@ PlacesController.prototype = {
|
|||
* Cut Bookmarks and Folders to the clipboard
|
||||
*/
|
||||
cut: function PC_cut() {
|
||||
this.copy();
|
||||
this.remove("Cut Selection");
|
||||
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() {
|
||||
// Strategy:
|
||||
//
|
||||
// There can be data of various types (folder, separator, link) on the
|
||||
// clipboard. We need to get all of that data and build edit transactions
|
||||
// for them. This means asking the clipboard once for each type and
|
||||
// aggregating the results.
|
||||
|
||||
/**
|
||||
* Constructs a transferable that can receive data of specific types.
|
||||
* @param types
|
||||
* The types of data the transferable can hold, in order of
|
||||
* preference.
|
||||
* @returns The transferable.
|
||||
*/
|
||||
function makeXferable(types) {
|
||||
var xferable = Cc["@mozilla.org/widget/transferable;1"].
|
||||
createInstance(Ci.nsITransferable);
|
||||
for (var i = 0; i < types.length; ++i)
|
||||
xferable.addDataFlavor(types[i]);
|
||||
return xferable;
|
||||
}
|
||||
|
||||
var clipboard = this.clipboard;
|
||||
|
||||
var ip = this._view.insertionPoint;
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* Gets a list of transactions to perform the paste of specific types.
|
||||
* @param types
|
||||
* The types of data to form paste transactions for
|
||||
* @returns An array of transactions that perform the paste.
|
||||
*/
|
||||
function getTransactions(types) {
|
||||
var xferable = makeXferable(types);
|
||||
clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
|
||||
let action = this.clipboardAction;
|
||||
|
||||
var data = { }, type = { };
|
||||
try {
|
||||
xferable.getAnyTransferData(type, data, { });
|
||||
data = data.value.QueryInterface(Ci.nsISupportsString).data;
|
||||
var items = PlacesUtils.unwrapNodes(data, type.value);
|
||||
var transactions = [];
|
||||
var index = ip.index;
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
var txn;
|
||||
if (ip.isTag) {
|
||||
var uri = PlacesUtils._uri(items[i].uri);
|
||||
txn = PlacesUIUtils.ptm.tagURI(uri, [ip.itemId]);
|
||||
}
|
||||
else {
|
||||
// adjusted to make sure that items are given the correct index
|
||||
// transactions insert differently if index == -1
|
||||
// transaction will enqueue the item.
|
||||
if (ip.index > -1)
|
||||
index = ip.index + i;
|
||||
txn = PlacesUIUtils.makeTransaction(items[i], type.value,
|
||||
ip.itemId, index, true);
|
||||
}
|
||||
transactions.push(txn);
|
||||
}
|
||||
return transactions;
|
||||
}
|
||||
catch (e) {
|
||||
// getAnyTransferData will throw if there is no data of the specified
|
||||
// type on the clipboard.
|
||||
// unwrapNodes will throw if the data that is present is malformed in
|
||||
// some way.
|
||||
// In either case, don't fail horribly, just return no data.
|
||||
}
|
||||
return [];
|
||||
let xferable = Cc["@mozilla.org/widget/transferable;1"].
|
||||
createInstance(Ci.nsITransferable);
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Get transactions to paste any folders, separators or links that might
|
||||
// be on the clipboard, aggregate them and execute them.
|
||||
var transactions = getTransactions([PlacesUtils.TYPE_X_MOZ_PLACE,
|
||||
PlacesUtils.TYPE_X_MOZ_URL,
|
||||
PlacesUtils.TYPE_UNICODE]);
|
||||
var txn = PlacesUIUtils.ptm.aggregateTransactions("Paste", transactions);
|
||||
PlacesUIUtils.ptm.doTransaction(txn);
|
||||
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.
|
||||
transactions.push(
|
||||
new PlacesTagURITransaction(PlacesUtils._uri(items[i].uri),
|
||||
[ip.itemId])
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// select the pasted items, they should be consecutive
|
||||
var insertedNodeIds = [];
|
||||
for (var i = 0; i < transactions.length; ++i)
|
||||
insertedNodeIds.push(PlacesUtils.bookmarks
|
||||
.getIdForItemAt(ip.itemId, ip.index + i));
|
||||
// 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")
|
||||
);
|
||||
}
|
||||
|
||||
PlacesUtils.transactionManager.doTransaction(
|
||||
new PlacesAggregatedTransaction("Paste", transactions)
|
||||
);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
|
|
@ -60,6 +60,14 @@
|
|||
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>
|
||||
|
||||
|
@ -333,6 +341,17 @@
|
|||
]]></getter>
|
||||
</property>
|
||||
|
||||
<method name="toggleCutNode">
|
||||
<parameter name="aNode"/>
|
||||
<parameter name="aValue"/>
|
||||
<body><![CDATA[
|
||||
aNode._cutting = aValue;
|
||||
try {
|
||||
this.view.nodeIconChanged(aNode);
|
||||
} catch(ex) { /* ignore no more valid nodes */ }
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- nsIPlacesView -->
|
||||
<property name="removableSelectionRanges">
|
||||
<getter><![CDATA[
|
||||
|
|
|
@ -1093,6 +1093,11 @@ PlacesTreeView.prototype = {
|
|||
return;
|
||||
|
||||
let node = this._getNodeForRow(aRow);
|
||||
|
||||
if (node._cutting) {
|
||||
aProperties.AppendElement(this._getAtomFor("cutting"));
|
||||
}
|
||||
|
||||
if (!node._cellProperties) {
|
||||
let properties = new Array();
|
||||
let itemId = node.itemId;
|
||||
|
|
|
@ -75,19 +75,6 @@ var PlacesUIUtils = {
|
|||
return URIFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wraps a string in a nsISupportsString wrapper
|
||||
* @param aString
|
||||
* The string to wrap
|
||||
* @returns A nsISupportsString object containing a string.
|
||||
*/
|
||||
_wrapString: function PUIU__wrapString(aString) {
|
||||
var s = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
s.data = aString;
|
||||
return s;
|
||||
},
|
||||
|
||||
getFormattedString: function PUIU_getFormattedString(key, params) {
|
||||
return bundle.formatStringFromName(key, params, params.length);
|
||||
},
|
||||
|
@ -814,9 +801,7 @@ var PlacesUIUtils = {
|
|||
browserWindow.whereToOpenLink(aEvent, false, true) : "window";
|
||||
if (where == "window") {
|
||||
// There is no browser window open, thus open a new one.
|
||||
var uriList = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
uriList.data = urls.join("|");
|
||||
var uriList = PlacesUtils.toISupportsString(urls.join("|"));
|
||||
var args = Cc["@mozilla.org/supports-array;1"].
|
||||
createInstance(Ci.nsISupportsArray);
|
||||
args.AppendElement(uriList);
|
||||
|
|
|
@ -73,6 +73,7 @@ _BROWSER_TEST_FILES = \
|
|||
browser_toolbar_migration.js \
|
||||
browser_library_batch_delete.js \
|
||||
browser_555547.js \
|
||||
browser_416459_cut.js \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_BROWSER_TEST_FILES)
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
const TEST_URL = "http://example.com/";
|
||||
|
||||
let gLibrary;
|
||||
let gItemId;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
gLibrary = openLibrary(onLibraryReady);
|
||||
}
|
||||
|
||||
function onLibraryReady(library) {
|
||||
PlacesOrganizer = gLibrary.PlacesOrganizer;
|
||||
|
||||
// Sanity checks.
|
||||
ok(PlacesUtils, "PlacesUtils in scope");
|
||||
ok(PlacesUIUtils, "PlacesUIUtils in scope");
|
||||
ok(PlacesOrganizer, "PlacesOrganizer in scope");
|
||||
|
||||
gItemId = PlacesUtils.bookmarks.insertBookmark(
|
||||
PlacesUtils.toolbarFolderId, NetUtil.newURI(TEST_URL),
|
||||
PlacesUtils.bookmarks.DEFAULT_INDEX, "test"
|
||||
);
|
||||
|
||||
selectBookmarkIn("BookmarksToolbar");
|
||||
|
||||
waitForClipboard(function(aData) !!aData,
|
||||
cutSelection,
|
||||
onClipboardReady,
|
||||
PlacesUtils.TYPE_X_MOZ_PLACE);
|
||||
}
|
||||
|
||||
function selectBookmarkIn(aLeftPaneQuery) {
|
||||
info("Selecting " + aLeftPaneQuery + " in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery(aLeftPaneQuery);
|
||||
let rootId = PlacesUtils.getConcreteItemId(PlacesOrganizer._places.selectedNode);
|
||||
is(PlacesUtils.bookmarks.getFolderIdForItem(gItemId), rootId,
|
||||
"Bookmark has the right parent");
|
||||
info("Selecting the bookmark in the right pane");
|
||||
PlacesOrganizer._content.selectItems([gItemId]);
|
||||
let bookmarkNode = PlacesOrganizer._content.selectedNode;
|
||||
is(bookmarkNode.uri, TEST_URL, "Found the expected bookmark");
|
||||
}
|
||||
|
||||
function cutSelection() {
|
||||
info("Cutting selection");
|
||||
PlacesOrganizer._content.controller.cut();
|
||||
}
|
||||
|
||||
function pasteClipboard(aLeftPaneQuery) {
|
||||
info("Selecting " + aLeftPaneQuery + " in the left pane");
|
||||
PlacesOrganizer.selectLeftPaneQuery(aLeftPaneQuery);
|
||||
info("Pasting clipboard");
|
||||
PlacesOrganizer._content.controller.paste();
|
||||
}
|
||||
|
||||
function onClipboardReady() {
|
||||
pasteClipboard("UnfiledBookmarks");
|
||||
selectBookmarkIn("UnfiledBookmarks");
|
||||
|
||||
// Cleanup.
|
||||
gLibrary.close();
|
||||
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
|
||||
finish();
|
||||
}
|
|
@ -16,9 +16,13 @@ registerCleanupFunction(function(){
|
|||
|
||||
|
||||
function openLibrary(callback) {
|
||||
var library = window.openDialog("chrome://browser/content/places/places.xul",
|
||||
let library = window.openDialog("chrome://browser/content/places/places.xul",
|
||||
"", "chrome,toolbar=yes,dialog=no,resizable");
|
||||
waitForFocus(function () {
|
||||
callback(library);
|
||||
}, library);
|
||||
|
||||
return library;
|
||||
}
|
||||
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
|
|
@ -230,6 +230,16 @@ menuitem.bookmark-item {
|
|||
list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
|
||||
}
|
||||
|
||||
.bookmark-item[cutting] > .toolbarbutton-icon,
|
||||
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.bookmark-item[cutting] > .toolbarbutton-text,
|
||||
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Stock icons for the menu bar items */
|
||||
menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
|
||||
-moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
|
||||
|
|
|
@ -99,13 +99,12 @@ treechildren::-moz-tree-image(title, query, folder) {
|
|||
list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-row(session-start) {
|
||||
border-top:1px dotted ThreeDShadow;
|
||||
font-weight: bold;
|
||||
treechildren::-moz-tree-image(cutting) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-cell-text(date, session-continue) {
|
||||
color: -moz-Field;
|
||||
treechildren::-moz-tree-cell-text(cutting) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/**** menuitem stock icons ****/
|
||||
|
|
|
@ -263,6 +263,16 @@ toolbarbutton.bookmark-item > menupopup {
|
|||
height: 16px;
|
||||
}
|
||||
|
||||
.bookmark-item[cutting] > .toolbarbutton-icon,
|
||||
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.bookmark-item[cutting] > .toolbarbutton-text,
|
||||
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
#bookmarksToolbarFolderMenu,
|
||||
#BMB_bookmarksToolbar {
|
||||
list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png");
|
||||
|
|
|
@ -119,10 +119,6 @@ treechildren::-moz-tree-image(title, separator) {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-row(session-start) {
|
||||
border-top:1px dotted ThreeDShadow;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(container, OrganizerQuery_AllBookmarks) {
|
||||
list-style-image: url("chrome://browser/skin/places/allBookmarks.png");
|
||||
}
|
||||
|
@ -178,12 +174,6 @@ treechildren::-moz-tree-image(title, query, folder, open) {
|
|||
-moz-image-region: rect(0, 16px, 16px, 0);
|
||||
}
|
||||
|
||||
/* FIXME this should make the date field invisible, but only does it for
|
||||
unselected items and maybe won't work for different color schemes. */
|
||||
treechildren::-moz-tree-cell-text(date, session-continue) {
|
||||
color:white;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-cell-text(title, separator) {
|
||||
color: ThreeDShadow;
|
||||
margin: 0px 5px;
|
||||
|
@ -197,3 +187,11 @@ treechildren::-moz-tree-twisty(title, separator) {
|
|||
-moz-appearance: none;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-image(cutting) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-cell-text(cutting) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
|
|
@ -566,6 +566,16 @@ menuitem.bookmark-item {
|
|||
-moz-image-region: rect(16px, 32px, 32px, 16px);
|
||||
}
|
||||
|
||||
.bookmark-item[cutting] > .toolbarbutton-icon,
|
||||
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.bookmark-item[cutting] > .toolbarbutton-text,
|
||||
.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* ::::: primary toolbar buttons ::::: */
|
||||
|
||||
.toolbarbutton-1 {
|
||||
|
|
|
@ -116,13 +116,12 @@ treechildren::-moz-tree-image(title, query, folder, open) {
|
|||
-moz-image-region: rect(16px, 32px, 32px, 16px);
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-row(session-start) {
|
||||
border-top:1px dotted ThreeDShadow;
|
||||
font-weight: bold;
|
||||
treechildren::-moz-tree-image(cutting) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
treechildren::-moz-tree-cell-text(date, session-continue) {
|
||||
color: -moz-Field;
|
||||
treechildren::-moz-tree-cell-text(cutting) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Browser Sidebars */
|
||||
|
|
|
@ -129,6 +129,8 @@ var PlacesUtils = {
|
|||
TYPE_HTML: "text/html",
|
||||
// Place entries as raw URL text
|
||||
TYPE_UNICODE: "text/unicode",
|
||||
// Used to track the action that populated the clipboard.
|
||||
TYPE_X_MOZ_PLACE_ACTION: "text/x-moz-place-action",
|
||||
|
||||
EXCLUDE_FROM_BACKUP_ANNO: "places/excludeFromBackup",
|
||||
GUID_ANNO: "placesInternal/GUID",
|
||||
|
@ -165,6 +167,19 @@ var PlacesUtils = {
|
|||
return NetUtil.newURI(aSpec);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wraps a string in a nsISupportsString wrapper.
|
||||
* @param aString
|
||||
* The string to wrap.
|
||||
* @returns A nsISupportsString object containing a string.
|
||||
*/
|
||||
toISupportsString: function PU_toISupportsString(aString) {
|
||||
let s = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
s.data = aString;
|
||||
return s;
|
||||
},
|
||||
|
||||
getFormattedString: function PU_getFormattedString(key, params) {
|
||||
return bundle.formatStringFromName(key, params, params.length);
|
||||
},
|
||||
|
|
|
@ -123,9 +123,7 @@
|
|||
catch (exc) {}
|
||||
|
||||
try {
|
||||
var str = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
str.data = "foo";
|
||||
var str = PlacesUtils.toISupportsString("foo");
|
||||
query = PlacesUtils.history.getNewQuery();
|
||||
query.tags = str;
|
||||
do_throw("Passing nsISupportsString to SetTags should fail");
|
||||
|
|
Загрузка…
Ссылка в новой задаче