Bug 613477 - Make the primary Star UI async.

r=sdwilsh ui-r=limi a=blocking
This commit is contained in:
Marco Bonardo 2010-11-22 20:34:57 +01:00
Родитель b82341a3bb
Коммит ac635aae45
7 изменённых файлов: 429 добавлений и 320 удалений

Просмотреть файл

@ -23,6 +23,7 @@
# Joe Hughes <joe@retrovirus.com>
# Asaf Romano <mano@mozilla.com>
# Ehsan Akhgari <ehsan.akhgari@gmail.com>
# Marco Bonardo <mak77@bonardo.net>
#
# 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
@ -920,7 +921,8 @@ var PlacesMenuDNDHandler = {
var PlacesStarButton = {
init: function PSB_init() {
init: function PSB_init()
{
try {
PlacesUtils.bookmarks.addObserver(this, false);
} catch(ex) {
@ -928,74 +930,139 @@ var PlacesStarButton = {
}
},
uninit: function PSB_uninit() {
uninit: function PSB_uninit()
{
try {
PlacesUtils.bookmarks.removeObserver(this);
} catch(ex) {}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver]),
QueryInterface: XPCOMUtils.generateQI([
Ci.nsINavBookmarkObserver
]),
_starred: false,
_batching: false,
get _starredTooltip()
{
delete this._starredTooltip;
return this._starredTooltip =
gNavigatorBundle.getString("starButtonOn.tooltip");
},
get _unstarredTooltip()
{
delete this._unstarredTooltip;
return this._unstarredTooltip =
gNavigatorBundle.getString("starButtonOff.tooltip");
},
updateState: function PSB_updateState() {
var starIcon = document.getElementById("star-button");
if (!starIcon)
updateState: function PSB_updateState()
{
this._starIcon = document.getElementById("star-button");
if (!this._starIcon || gBrowser.currentURI.equals(this._uri)) {
return;
var uri = gBrowser.currentURI;
this._starred = uri && (PlacesUtils.getMostRecentBookmarkForURI(uri) != -1 ||
PlacesUtils.getMostRecentFolderForFeedURI(uri) != -1);
if (this._starred) {
starIcon.setAttribute("starred", "true");
starIcon.setAttribute("tooltiptext", gNavigatorBundle.getString("starButtonOn.tooltip"));
}
else {
starIcon.removeAttribute("starred");
starIcon.setAttribute("tooltiptext", gNavigatorBundle.getString("starButtonOff.tooltip"));
// Reset tracked values.
this._uri = gBrowser.currentURI;
this._itemIds = [];
// Hide the star while we update its state.
this._starIcon.hidden = true;
PlacesUtils.asyncGetBookmarkIds(this._uri, function (aItemIds) {
this._itemIds = aItemIds;
this._updateStateInternal();
// Finally show the star.
this._starIcon.hidden = false;
}, this);
},
_updateStateInternal: function PSB__updateStateInternal()
{
if (!this._starIcon) {
return;
}
let starred = this._starIcon.hasAttribute("starred");
if (this._itemIds.length > 0 && !starred) {
this._starIcon.setAttribute("starred", "true");
this._starIcon.setAttribute("tooltiptext", this._starredTooltip);
}
else if (this._itemIds.length == 0 && starred) {
this._starIcon.removeAttribute("starred");
this._starIcon.setAttribute("tooltiptext", this._unstarredTooltip);
}
},
onClick: function PSB_onClick(aEvent) {
if (aEvent.button == 0)
PlacesCommandHook.bookmarkCurrentPage(this._starred);
// don't bubble to the textbox so that the address won't be selected
onClick: function PSB_onClick(aEvent)
{
if (aEvent.button == 0) {
PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0);
}
// Don't bubble to the textbox, to avoid unwanted selection of the address.
aEvent.stopPropagation();
},
// nsINavBookmarkObserver
onBeginUpdateBatch: function PSB_onBeginUpdateBatch() {
this._batching = true;
onItemAdded:
function PSB_onItemAdded(aItemId, aFolder, aIndex, aItemType, aURI)
{
if (!this._starIcon) {
return;
}
if (aURI.equals(this._uri)) {
// If a new bookmark has been added to the tracked uri, register it.
if (this._itemIds.indexOf(aItemId) == -1) {
this._itemIds.push(aItemId);
this._updateStateInternal();
}
}
},
onEndUpdateBatch: function PSB_onEndUpdateBatch() {
this.updateState();
this._batching = false;
onItemRemoved:
function PSB_onItemRemoved(aItemId, aFolder, aIndex, aItemType)
{
if (!this._starIcon) {
return;
}
let index = this._itemIds.indexOf(aItemId);
// If one of the tracked bookmarks has been removed, unregister it.
if (index != -1) {
this._itemIds.splice(index, 1);
this._updateStateInternal();
}
},
onItemAdded: function PSB_onItemAdded(aItemId, aFolder, aIndex, aItemType,
aURI) {
if (!this._batching && !this._starred)
this.updateState();
onItemChanged:
function PSB_onItemChanged(aItemId, aProperty, aIsAnnotationProperty,
aNewValue, aLastModified, aItemType)
{
if (!this._starIcon) {
return;
}
if (aProperty == "uri") {
let index = this._itemIds.indexOf(aItemId);
// If the changed bookmark was tracked, check if it is now pointing to
// a different uri and unregister it.
if (index != -1 && aNewValue != this._uri.spec) {
this._itemIds.splice(index, 1);
this._updateStateInternal();
}
// If another bookmark is now pointing to the tracked uri, register it.
else if (index == -1 && aNewValue == this._uri.spec) {
this._itemIds.push(aItemId);
this._updateStateInternal();
}
}
},
onBeforeItemRemoved: function() {},
onItemRemoved: function PSB_onItemRemoved(aItemId, aFolder, aIndex,
aItemType) {
if (!this._batching)
this.updateState();
},
onItemChanged: function PSB_onItemChanged(aItemId, aProperty,
aIsAnnotationProperty, aNewValue,
aLastModified, aItemType) {
if (!this._batching && aProperty == "uri")
this.updateState();
},
onItemVisited: function() {},
onItemMoved: function() {}
onBeginUpdateBatch: function () {},
onEndUpdateBatch: function () {},
onBeforeItemRemoved: function () {},
onItemVisited: function () {},
onItemMoved: function () {}
};

Просмотреть файл

@ -572,6 +572,7 @@
<hbox id="urlbar-icons">
<image id="star-button"
class="urlbar-icon"
hidden="true"
onclick="PlacesStarButton.onClick(event);"/>
<image id="go-button"
class="urlbar-icon"

Просмотреть файл

@ -31,28 +31,47 @@ function invokeUsingStarButton(phase) {
}
var testURL = "data:text/plain,Content";
var bookmarkId;
function add_bookmark(aURI, aTitle) {
return PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
aURI, PlacesUtils.bookmarks.DEFAULT_INDEX,
aTitle);
}
// test bug 432599
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", initTest, true);
gBrowser.selectedBrowser.addEventListener("load", function () {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
waitForStarChange(false, initTest);
}, true);
content.location = testURL;
}
let invokers = [invokeUsingStarButton, invokeUsingCtrlD];
let currentInvoker = 0;
function initTest() {
gBrowser.selectedBrowser.removeEventListener("load", initTest, true);
// first, bookmark the page
Application.bookmarks.toolbar.addBookmark("Bug 432599 Test", makeURI(testURL));
// First, bookmark the page.
bookmarkId = add_bookmark(makeURI(testURL), "Bug 432599 Test");
checkBookmarksPanel(invokers[currentInvoker], 1);
}
function waitForStarChange(aValue, aCallback) {
let starButton = document.getElementById("star-button");
if (starButton.hidden || starButton.hasAttribute("starred") != aValue) {
info("Waiting for star button change.");
setTimeout(arguments.callee, 50, aValue, aCallback);
return;
}
aCallback();
}
let invokers = [invokeUsingStarButton, invokeUsingCtrlD];
let currentInvoker = 0;
let initialValue;
let initialRemoveHidden;
@ -64,12 +83,13 @@ function checkBookmarksPanel(invoker, phase)
{
let onPopupShown = function(aEvent) {
if (aEvent.originalTarget == popupElement) {
popupElement.removeEventListener("popupshown", arguments.callee, false);
checkBookmarksPanel(invoker, phase + 1);
popupElement.removeEventListener("popupshown", onPopupShown, false);
}
};
let onPopupHidden = function(aEvent) {
if (aEvent.originalTarget == popupElement) {
popupElement.removeEventListener("popuphidden", arguments.callee, false);
if (phase < 4) {
checkBookmarksPanel(invoker, phase + 1);
} else {
@ -78,10 +98,10 @@ function checkBookmarksPanel(invoker, phase)
checkBookmarksPanel(invokers[currentInvoker], 1);
} else {
gBrowser.removeCurrentTab();
finish();
PlacesUtils.bookmarks.removeItem(bookmarkId);
executeSoon(finish);
}
}
popupElement.removeEventListener("popuphidden", onPopupHidden, false);
}
};

Просмотреть файл

@ -21,19 +21,33 @@ function test() {
PlacesUtils.transactionManager.doTransaction(bmTxn);
ok(PlacesUtils.bookmarks.isBookmarked(uri), "the test url is bookmarked");
ok(starButton.getAttribute("starred") == "true",
"star button indicates that the page is bookmarked");
let tagTxn = new PlacesTagURITransaction(uri, [testTag]);
PlacesUtils.transactionManager.doTransaction(tagTxn);
StarUI.panel.addEventListener("popupshown", onPanelShown, false);
starButton.click();
waitForStarChange(true, onStarred);
}), true);
content.location = testURL;
}
function waitForStarChange(aValue, aCallback) {
let starButton = document.getElementById("star-button");
if (starButton.hidden || starButton.hasAttribute("starred") != aValue) {
info("Waiting for star button change.");
setTimeout(arguments.callee, 50, aValue, aCallback);
return;
}
aCallback();
}
function onStarred() {
ok(starButton.getAttribute("starred") == "true",
"star button indicates that the page is bookmarked");
let uri = makeURI(testURL);
let tagTxn = new PlacesTagURITransaction(uri, [testTag]);
PlacesUtils.transactionManager.doTransaction(tagTxn);
StarUI.panel.addEventListener("popupshown", onPanelShown, false);
starButton.click();
}
function onPanelShown(aEvent) {
if (aEvent.target == StarUI.panel) {

Просмотреть файл

@ -1,124 +1,99 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** 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 Places test code.
*
* The Initial Developer of the Original Code is Mozilla Corp.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* David Dahl <ddahl@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 ***** */
// Get history services
var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var gh = hs.QueryInterface(Ci.nsIGlobalHistory2);
var bh = hs.QueryInterface(Ci.nsIBrowserHistory);
var ts = Cc["@mozilla.org/browser/tagging-service;1"].
getService(Components.interfaces.nsITaggingService);
var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
function add_visit(aURI, aReferrer) {
var visitId = hs.addVisit(aURI,
Date.now() * 1000,
aReferrer,
hs.TRANSITION_TYPED, // user typed in URL bar
false, // not redirect
0);
return visitId;
return PlacesUtils.history.addVisit(aURI, Date.now() * 1000, aReferrer,
PlacesUtils.history.TRANSITION_TYPED,
false, 0);
}
function add_bookmark(aURI) {
var bId = bs.insertBookmark(bs.unfiledBookmarksFolder, aURI,
bs.DEFAULT_INDEX, "bookmark/" + aURI.spec);
return bId;
return PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
aURI, PlacesUtils.bookmarks.DEFAULT_INDEX,
"bookmark/" + aURI.spec);
}
Components.utils.import("resource://gre/modules/NetUtil.jsm");
const TEST_URL = "http://example.com/";
const MOZURISPEC = "http://mozilla.com/";
let gLibrary;
let PlacesOrganizer;
function test() {
waitForExplicitFinish();
var win = window.openDialog("chrome://browser/content/places/places.xul",
"",
"chrome,toolbar=yes,dialog=no,resizable");
gLibrary = window.openDialog("chrome://browser/content/places/places.xul",
"", "chrome,toolbar=yes,dialog=no,resizable");
waitForFocus(onLibraryReady, gLibrary);
}
win.addEventListener("load", function onload() {
win.removeEventListener("load", onload, false);
executeSoon(function () {
var PU = win.PlacesUtils;
var PO = win.PlacesOrganizer;
var PUIU = win.PlacesUIUtils;
function onLibraryReady() {
ok(PlacesUtils, "PlacesUtils in scope");
ok(PlacesUIUtils, "PlacesUIUtils in scope");
// individual tests for each step of tagging a history item
var tests = {
PlacesOrganizer = gLibrary.PlacesOrganizer;
ok(PlacesOrganizer, "Places organizer in scope");
sanity: function(){
// sanity check
ok(PU, "PlacesUtils in scope");
ok(PUIU, "PlacesUIUtils in scope");
ok(PO, "Places organizer in scope");
},
tests.makeHistVisit();
tests.makeTag();
tests.focusTag();
tests.copyHistNode();
tests.waitForClipboard();
}
function onClipboardReady() {
tests.pasteToTag();
tests.historyNode();
tests.checkForBookmarkInUI();
gLibrary.close();
// Remove new Places data we created.
PlacesUtils.tagging.untagURI(NetUtil.newURI(MOZURISPEC), ["foo"]);
PlacesUtils.tagging.untagURI(NetUtil.newURI(TEST_URL), ["foo"]);
let tags = PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(TEST_URL));
is(tags.length, 0, "tags are gone");
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
waitForClearHistory(finish);
}
let tests = {
makeHistVisit: function() {
// need to add a history object
var testURI1 = PU._uri(MOZURISPEC);
let testURI1 = NetUtil.newURI(MOZURISPEC);
isnot(testURI1, null, "testURI is not null");
var visitId = add_visit(testURI1);
let visitId = add_visit(testURI1);
ok(visitId > 0, "A visit was added to the history");
ok(gh.isVisited(testURI1), MOZURISPEC + " is a visited url.");
ok(PlacesUtils.ghistory2.isVisited(testURI1), MOZURISPEC + " is a visited url.");
},
makeTag: function() {
// create an initial tag to work with
var bmId = add_bookmark(PlacesUtils._uri(TEST_URL));
let bmId = add_bookmark(NetUtil.newURI(TEST_URL));
ok(bmId > 0, "A bookmark was added");
ts.tagURI(PlacesUtils._uri(TEST_URL), ["foo"]);
var tags = ts.getTagsForURI(PU._uri(TEST_URL));
PlacesUtils.tagging.tagURI(NetUtil.newURI(TEST_URL), ["foo"]);
let tags = PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(TEST_URL));
is(tags[0], 'foo', "tag is foo");
},
focusTag: function (paste){
// focus the new tag
PO.selectLeftPaneQuery("Tags");
var tags = PO._places.selectedNode;
PlacesOrganizer.selectLeftPaneQuery("Tags");
let tags = PlacesOrganizer._places.selectedNode;
tags.containerOpen = true;
var fooTag = tags.getChild(0);
let fooTag = tags.getChild(0);
this.tagNode = fooTag;
PO._places.selectNode(fooTag);
PlacesOrganizer._places.selectNode(fooTag);
is(this.tagNode.title, 'foo', "tagNode title is foo");
var ip = PO._places.insertionPoint;
let ip = PlacesOrganizer._places.insertionPoint;
ok(ip.isTag, "IP is a tag");
if (paste) {
ok(true, "About to paste");
PO._places.controller.paste();
PlacesOrganizer._places.controller.paste();
}
},
@ -126,33 +101,34 @@ function test() {
copyHistNode: function (){
// focus the history object
PO.selectLeftPaneQuery("History");
var histContainer = PO._places.selectedNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
PlacesOrganizer.selectLeftPaneQuery("History");
let histContainer = PlacesOrganizer._places.selectedNode;
PlacesUtils.asContainer(histContainer);
histContainer.containerOpen = true;
PO._places.selectNode(histContainer.getChild(0));
this.histNode = PO._content.view.nodeForTreeIndex(0);
PO._content.selectNode(this.histNode);
PlacesOrganizer._places.selectNode(histContainer.getChild(0));
this.histNode = PlacesOrganizer._content.view.nodeForTreeIndex(0);
PlacesOrganizer._content.selectNode(this.histNode);
is(this.histNode.uri, MOZURISPEC,
"historyNode exists: " + this.histNode.uri);
// copy the history node
PO._content.controller.copy();
PlacesOrganizer._content.controller.copy();
},
waitForPaste: function (){
waitForClipboard: function (){
try {
var xferable = Cc["@mozilla.org/widget/transferable;1"].
let xferable = Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
xferable.addDataFlavor(PU.TYPE_X_MOZ_PLACE);
var clipboard = Cc["@mozilla.org/widget/clipboard;1"].
xferable.addDataFlavor(PlacesUtils.TYPE_X_MOZ_PLACE);
let clipboard = Cc["@mozilla.org/widget/clipboard;1"].
getService(Ci.nsIClipboard);
clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
var data = { }, type = { };
let data = { }, type = { };
xferable.getAnyTransferData(type, data, { });
// Data is in the clipboard
continue_test();
onClipboardReady();
} catch (ex) {
// check again after 100ms.
setTimeout(tests.waitForPaste, 100);
setTimeout(arguments.callee, 100);
}
},
@ -163,64 +139,48 @@ function test() {
historyNode: function (){
// re-focus the history again
PO.selectLeftPaneQuery("History");
var histContainer = PO._places.selectedNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
PlacesOrganizer.selectLeftPaneQuery("History");
let histContainer = PlacesOrganizer._places.selectedNode;
PlacesUtils.asContainer(histContainer);
histContainer.containerOpen = true;
PO._places.selectNode(histContainer.getChild(0));
var histNode = PO._content.view.nodeForTreeIndex(0);
PlacesOrganizer._places.selectNode(histContainer.getChild(0));
let histNode = PlacesOrganizer._content.view.nodeForTreeIndex(0);
ok(histNode, "histNode exists: " + histNode.title);
// check to see if the history node is tagged!
var tags = PU.tagging.getTagsForURI(PU._uri(MOZURISPEC));
let tags = PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(MOZURISPEC));
ok(tags.length == 1, "history node is tagged: " + tags.length);
// check if a bookmark was created
var isBookmarked = PU.bookmarks.isBookmarked(PU._uri(MOZURISPEC));
let isBookmarked = PlacesUtils.bookmarks.isBookmarked(NetUtil.newURI(MOZURISPEC));
is(isBookmarked, true, MOZURISPEC + " is bookmarked");
var bookmarkIds = PU.bookmarks.getBookmarkIdsForURI(
PU._uri(histNode.uri));
let bookmarkIds = PlacesUtils.bookmarks.getBookmarkIdsForURI(
NetUtil.newURI(histNode.uri));
ok(bookmarkIds.length > 0, "bookmark exists for the tagged history item: " + bookmarkIds);
},
checkForBookmarkInUI: function(){
// is the bookmark visible in the UI?
// get the Unsorted Bookmarks node
PO.selectLeftPaneQuery("UnfiledBookmarks");
PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
// now we can see what is in the _content tree
var unsortedNode = PO._content.view.nodeForTreeIndex(1);
let unsortedNode = PlacesOrganizer._content.view.nodeForTreeIndex(1);
ok(unsortedNode, "unsortedNode is not null: " + unsortedNode.uri);
is(unsortedNode.uri, MOZURISPEC, "node uri's are the same");
},
tagNode: null,
};
cleanUp: function(){
ts.untagURI(PU._uri(MOZURISPEC), ["foo"]);
ts.untagURI(PU._uri(TEST_URL), ["foo"]);
hs.removeAllPages();
var tags = ts.getTagsForURI(PU._uri(TEST_URL));
is(tags.length, 0, "tags are gone");
bs.removeFolderChildren(bs.unfiledBookmarksFolder);
/**
* Clears history invoking callback when done.
*/
function waitForClearHistory(aCallback) {
const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
let observer = {
observe: function(aSubject, aTopic, aData) {
Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
aCallback();
}
};
tests.sanity();
tests.makeHistVisit();
tests.makeTag();
tests.focusTag();
tests.copyHistNode();
tests.waitForPaste();
function continue_test() {
tests.pasteToTag();
tests.historyNode();
tests.checkForBookmarkInUI();
// remove new places data we created
tests.cleanUp();
win.close();
finish();
}
});
},false);
Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED, false);
PlacesUtils.bhistory.removeAllPages();
}

Просмотреть файл

@ -82,10 +82,6 @@ XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
return NetUtil;
});
// Global observers flags.
let gHasAnnotationsObserver = false;
let gHasShutdownObserver = false;
// The minimum amount of transactions before starting a batch. Usually we do
// do incremental updates, a batch will cause views to completely
// refresh instead.
@ -275,15 +271,11 @@ var PlacesUtils = {
*/
get _readOnly() {
// Add annotations observer.
if (!gHasAnnotationsObserver) {
this.annotations.addObserver(this, false);
gHasAnnotationsObserver = true;
}
// Observe shutdown, so we can remove the anno observer.
if (!gHasShutdownObserver) {
Services.obs.addObserver(this, this.TOPIC_SHUTDOWN, false);
gHasShutdownObserver = true;
}
this.registerShutdownFunction(function () {
this.annotations.removeObserver(this);
});
var readOnly = this.annotations.getItemsWithAnnotation(this.READ_ONLY_ANNO);
this.__defineGetter__("_readOnly", function() readOnly);
return this._readOnly;
@ -295,24 +287,25 @@ var PlacesUtils = {
, Ci.nsITransactionListener
]),
// nsIObserver
observe: function PU_observe(aSubject, aTopic, aData) {
if (aTopic == this.TOPIC_SHUTDOWN) {
if (gHasAnnotationsObserver)
this.annotations.removeObserver(this);
if (Object.getOwnPropertyDescriptor(this, "transactionManager").value !== undefined) {
// Clear all references to local transactions in the transaction manager,
// this prevents from leaking it.
this.transactionManager.RemoveListener(this);
this.transactionManager.clear();
}
Services.obs.removeObserver(this, this.TOPIC_SHUTDOWN);
gHasShutdownObserver = false;
_shutdownFunctions: [],
registerShutdownFunction: function PU_registerShutdownFunction(aFunc)
{
// If this is the first registered function, add the shutdown observer.
if (this._shutdownFunctions.length == 0) {
Services.obs.addObserver(this, this.TOPIC_SHUTDOWN, false);
}
this._shutdownFunctions.push(aFunc);
},
//////////////////////////////////////////////////////////////////////////////
//// nsIObserver
observe: function PU_observe(aSubject, aTopic, aData)
{
if (aTopic == this.TOPIC_SHUTDOWN) {
Services.obs.removeObserver(this, this.TOPIC_SHUTDOWN);
this._shutdownFunctions.forEach(function (aFunc) aFunc.apply(this), this);
}
},
//////////////////////////////////////////////////////////////////////////////
//// nsIAnnotationObserver
@ -2048,6 +2041,63 @@ var PlacesUtils = {
}
},
/**
* Given a uri returns list of itemIds associated to it.
*
* @param aURI
* nsIURI or spec of the page.
* @param aCallback
* Function to be called when done.
* The function will receive an array of itemIds associated to aURI.
* @param aScope
* Scope for the callback.
*
* @note Children of live bookmarks folders are excluded.
*/
asyncGetBookmarkIds: function PU_asyncGetBookmarkIds(aURI, aCallback, aScope)
{
if (!this._asyncGetBookmarksStmt) {
let db = this.history.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
this._asyncGetBookmarksStmt = db.createAsyncStatement(
"SELECT b.id "
+ "FROM moz_bookmarks b "
+ "JOIN moz_places h on h.id = b.fk "
+ "WHERE h.url = :url "
+ "AND NOT EXISTS( "
+ "SELECT 1 FROM moz_items_annos a "
+ "JOIN moz_anno_attributes n ON a.anno_attribute_id = n.id "
+ "WHERE a.item_id = b.parent AND n.name = :name "
+ ") "
);
this.registerShutdownFunction(function () {
this._asyncGetBookmarksStmt.finalize();
});
}
let url = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
this._asyncGetBookmarksStmt.params.url = url;
this._asyncGetBookmarksStmt.params.name = this.LMANNO_FEEDURI;
this._asyncGetBookmarksStmt.executeAsync({
_itemIds: [],
handleResult: function(aResultSet) {
let row, haveMatches = false;
for (let row; (row = aResultSet.getNextRow());) {
this._itemIds.push(row.getResultByIndex(0));
}
},
handleError: function(aError) {
Cu.reportError("Async statement execution returned (" + aError.result +
"): " + aError.message);
},
handleCompletion: function(aReason)
{
if (aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED) {
aCallback.apply(aScope, [this._itemIds]);
}
}
});
}
};
XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "history",
@ -2091,18 +2141,15 @@ XPCOMUtils.defineLazyServiceGetter(PlacesUtils, "microsummaries",
"nsIMicrosummaryService");
XPCOMUtils.defineLazyGetter(PlacesUtils, "transactionManager", function() {
Services.obs.addObserver(PlacesUtils,
PlacesUtils.TOPIC_SHUTDOWN,
false);
// Observe shutdown, so we can remove the anno observer.
if (!gHasShutdownObserver) {
Services.obs.addObserver(PlacesUtils, PlacesUtils.TOPIC_SHUTDOWN, false);
gHasShutdownObserver = true;
}
let tm = Cc["@mozilla.org/transactionmanager;1"].
getService(Ci.nsITransactionManager);
tm.AddListener(PlacesUtils);
this.registerShutdownFunction(function () {
// Clear all references to local transactions in the transaction manager,
// this prevents from leaking it.
this.transactionManager.RemoveListener(this);
this.transactionManager.clear();
});
return tm;
});

Просмотреть файл

@ -193,7 +193,7 @@ TaggingService.prototype = {
{
if (tag.id == -1) {
// Tag does not exist yet, create it.
tag.id = this._createTag(tag.name);
this._createTag(tag.name);
}
if (this._getItemIdForTaggedURI(aURI, tag.name) == -1) {