зеркало из https://github.com/mozilla/gecko-dev.git
Added nsIBookmarksContainer interface which allows custom bookmarks types.
Implemented nsILivemarksService as a nsIBookmarksContainer. bug=317837 r=beng sr=bryner
This commit is contained in:
Родитель
ea5ff41b60
Коммит
b2ff11200f
|
@ -105,3 +105,9 @@
|
|||
|
||||
#define NS_FAVICONSERVICE_CONTRACTID \
|
||||
"@mozilla.org/browser/favicon-service;1"
|
||||
|
||||
#define NS_LIVEMARKSERVICE_CID \
|
||||
{ 0xb1257934, 0x86cf, 0x4143, { 0x83, 0x86, 0x73, 0x4a, 0xc3, 0x52, 0xb6, 0xba } }
|
||||
|
||||
#define NS_LIVEMARKSERVICE_CONTRACTID \
|
||||
"@mozilla.org/browser/livemark-service;1"
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "nsNavHistory.h"
|
||||
#include "nsNavBookmarks.h"
|
||||
#include "nsFaviconService.h"
|
||||
#include "nsLivemarkService.h"
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
#include "nsWindowsShellService.h"
|
||||
|
@ -85,6 +86,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsAnnoProtocolHandler)
|
|||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsAnnotationService, Init)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNavBookmarks, Init)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsFaviconService, Init)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLivemarkService, Init)
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindowsShellService)
|
||||
|
@ -165,6 +167,11 @@ static const nsModuleComponentInfo components[] =
|
|||
NS_FAVICONSERVICE_CID,
|
||||
NS_FAVICONSERVICE_CONTRACTID,
|
||||
nsFaviconServiceConstructor },
|
||||
|
||||
{ "Livemark Service",
|
||||
NS_LIVEMARKSERVICE_CID,
|
||||
NS_LIVEMARKSERVICE_CONTRACTID,
|
||||
nsLivemarkServiceConstructor },
|
||||
#endif
|
||||
|
||||
{ "Bookmarks",
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
var PlacesBrowserShim = {
|
||||
_bms: null,
|
||||
_lms: null,
|
||||
_history: null,
|
||||
};
|
||||
|
||||
|
@ -47,6 +48,13 @@ PlacesBrowserShim.init = function PBS_init() {
|
|||
this._bms =
|
||||
Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
|
||||
getService(Ci.nsINavBookmarksService);
|
||||
|
||||
this._lms =
|
||||
Cc["@mozilla.org/browser/livemark-service;1"].
|
||||
getService(Ci.nsILivemarkService);
|
||||
|
||||
// Override the old addLivemark function
|
||||
BookmarksUtils.addLivemark = function(a,b,c,d) {PlacesBrowserShim.addLivemark(a,b,c,d);};
|
||||
|
||||
var newMenuPopup = document.getElementById("bookmarksMenuPopup");
|
||||
newMenuPopup.folderId = this._bms.bookmarksRoot;
|
||||
|
@ -68,4 +76,17 @@ PlacesBrowserShim.showPlaces = function PBS_showPlaces() {
|
|||
loadURI("chrome://browser/content/places/places.xul", null, null);
|
||||
};
|
||||
|
||||
PlacesBrowserShim.addLivemark = function PBS_addLivemark(aURL, aFeedURL, aTitle, aDescription) {
|
||||
// TODO -- put in nice confirmation dialog.
|
||||
var ios =
|
||||
Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
|
||||
this._lms.createLivemark(this._bms.toolbarRoot,
|
||||
aTitle,
|
||||
ios.newURI(aURL, null, null),
|
||||
ios.newURI(aFeedURL, null, null),
|
||||
-1);
|
||||
};
|
||||
|
||||
addEventListener("load", function () { PlacesBrowserShim.init(); }, false);
|
||||
|
|
|
@ -420,6 +420,16 @@ var PlacesController = {
|
|||
return node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_HOST;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines whether or not a ResultNode is a container item or not
|
||||
* @param node
|
||||
* A NavHistoryResultNode
|
||||
* @returns true if the node is a container item, false otherwise
|
||||
*/
|
||||
nodeIsContainer: function PC_nodeIsContainer(node) {
|
||||
return node.folderType != "";
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates commands on focus/selection change to reflect the enabled/
|
||||
* disabledness of commands in relation to the state of the selection.
|
||||
|
@ -480,7 +490,7 @@ var PlacesController = {
|
|||
* is-mutable "mutable"
|
||||
* is-removable "removable"
|
||||
* is-multiselect"multiselect"
|
||||
* is-livemark "livemark"
|
||||
* is-container "container"
|
||||
* @returns an object with each of the properties above set if the selection
|
||||
* matches that rule.
|
||||
*/
|
||||
|
@ -493,6 +503,8 @@ var PlacesController = {
|
|||
var selectedNode = this._activeView.selectedNode;
|
||||
if (this.nodeIsFolder(selectedNode) && hasSingleSelection)
|
||||
metadata["folder"] = true;
|
||||
if (this.nodeIsContainer(selectedNode) && hasSingleSelection)
|
||||
metadata["container"] = true;
|
||||
|
||||
var foundNonLeaf = false;
|
||||
var nodes = this._activeView.getSelectionNodes();
|
||||
|
@ -500,8 +512,7 @@ var PlacesController = {
|
|||
var node = nodes[i];
|
||||
if (node.type != Ci.nsINavHistoryResultNode.RESULT_TYPE_URL)
|
||||
foundNonLeaf = true;
|
||||
// XXXben check for livemarkness
|
||||
if (!node.readonly)
|
||||
if (!node.readonly && node.folderType == "")
|
||||
metadata["mutable"] = true;
|
||||
}
|
||||
if (this._activeView.getAttribute("seltype") != "single")
|
||||
|
|
|
@ -36,6 +36,15 @@
|
|||
options.setGroupingMode([Ci.nsINavHistoryQueryOptions.GROUP_BY_FOLDER], 1);
|
||||
// options.setExpandPlaces(); ?
|
||||
this._result = this._places.executeQuery(query, options);
|
||||
|
||||
// If this is a container, notify the service.
|
||||
if (PlacesController.nodeIsContainer(this._result)) {
|
||||
var serv = Cc[this._result.folderType]
|
||||
.getService(Ci.nsIBookmarksContainer);
|
||||
if (serv.onContainerOpening(this._result.folderId))
|
||||
this._result = this._places.executeQuery(query, options);
|
||||
}
|
||||
|
||||
this._rebuild();
|
||||
if (this.popupShowingCallback)
|
||||
this.popupShowingCallback();
|
||||
|
@ -240,8 +249,19 @@
|
|||
</implementation>
|
||||
<handlers>
|
||||
<handler event="popupshowing">
|
||||
if (event.target == this)
|
||||
if (event.target == this) {
|
||||
this.onPopupShowing();
|
||||
}
|
||||
</handler>
|
||||
<handler event="popuphidden">
|
||||
if (event.target == this) {
|
||||
// If this is a container, notify the service.
|
||||
if (PlacesController.nodeIsContainer(this._result)) {
|
||||
var serv = Cc[this._result.folderType]
|
||||
.getService(Ci.nsIBookmarksContainer);
|
||||
serv.onContainerClosed(this._result.folderId);
|
||||
}
|
||||
}
|
||||
</handler>
|
||||
<handler event="click">
|
||||
if (event.target.localName != "menuitem")
|
||||
|
|
|
@ -35,6 +35,16 @@
|
|||
var options = this._places.getNewQueryOptions();
|
||||
options.setGroupingMode([Ci.nsINavHistoryQueryOptions.GROUP_BY_FOLDER], 1);
|
||||
this._result = this._places.executeQuery(query, options);
|
||||
|
||||
// Check if the new query needs a show event.
|
||||
if (PlacesController.nodeIsContainer(this._result)) {
|
||||
var serv = Cc[this._result.folderType]
|
||||
.getService(Ci.nsIBookmarksContainer);
|
||||
if (serv.onContainerOpening(this._result.folderId))
|
||||
this._result = this._places.executeQueries(queries, queries.length,
|
||||
options);
|
||||
}
|
||||
|
||||
this._rebuild();
|
||||
]]></body>
|
||||
</method>
|
||||
|
@ -153,9 +163,25 @@
|
|||
<method name="load">
|
||||
<parameter name="queries"/>
|
||||
<parameter name="options"/>
|
||||
<body><![CDATA[
|
||||
<body><![CDATA[
|
||||
// Check if the old query needs a hidden event.
|
||||
if (PlacesController.nodeIsContainer(this._result)) {
|
||||
var serv = Cc[this._result.folderType]
|
||||
.getService(Ci.nsIBookmarksContainer);
|
||||
serv.onContainerClosed(this._result.folderId);
|
||||
}
|
||||
|
||||
this._result = this._places.executeQueries(queries, queries.length,
|
||||
options);
|
||||
|
||||
// Check if the new query needs a show event.
|
||||
if (PlacesController.nodeIsContainer(this._result)) {
|
||||
var serv = Cc[this._result.folderType]
|
||||
.getService(Ci.nsIBookmarksContainer);
|
||||
if (serv.onContainerOpening(this._result.folderId))
|
||||
this._result = this._places.executeQueries(queries, queries.length,
|
||||
options);
|
||||
}
|
||||
this._rebuild();
|
||||
]]></body>
|
||||
</method>
|
||||
|
|
|
@ -486,9 +486,22 @@
|
|||
visibleInsertCount);
|
||||
},
|
||||
|
||||
onToggleOpenState: function VO_onToggleOpenState(index) {
|
||||
// XXXben we will probably have to implement this to refresh Live
|
||||
// Bookmarks.
|
||||
onToggleOpenState: function VO_onToggleOpenState(index) {
|
||||
// Alert containers about the toggle
|
||||
var result = this._self.getResult();
|
||||
var node = result.nodeForTreeIndex(index);
|
||||
if (PlacesController.nodeIsContainer(node)) {
|
||||
var serv = Cc[node.folderType].getService(Ci.nsIBookmarksContainer);
|
||||
// This notification happens before the container is actually
|
||||
// opened or closed, so the open state is the opposite of what
|
||||
// we're notifying.
|
||||
if (this._self.view.isContainerOpen(index)) {
|
||||
serv.onContainerClosed(node.folderId);
|
||||
} else {
|
||||
if (serv.onContainerOpening(node.folderId))
|
||||
this._self.loadFolder(node.folderId);
|
||||
}
|
||||
}
|
||||
},
|
||||
onSelectionChanged: function VO_onSelectionChanged() { },
|
||||
onCycleHeader: function VO_onCycleHeader(column) { },
|
||||
|
|
|
@ -20,6 +20,8 @@ newFolderDefault=New Folder
|
|||
|
||||
bookmarksMenuName=Bookmarks Menu
|
||||
bookmarksToolbarName=Bookmarks Toolbar
|
||||
bookmarksLivemarkLoading=Live Bookmark loading...
|
||||
bookmarksLivemarkFailed=Live Bookmark feed failed to load.
|
||||
|
||||
findPlaceLabel=Find in Places...
|
||||
findPageLabel=Find in This Page...
|
||||
|
|
|
@ -51,6 +51,9 @@ XPIDLSRCS = \
|
|||
nsIFaviconService.idl \
|
||||
nsINavHistory.idl \
|
||||
nsINavBookmarksService.idl \
|
||||
nsIBookmarksContainer.idl \
|
||||
nsILivemarkService.idl \
|
||||
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; 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 Places.
|
||||
*
|
||||
* 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):
|
||||
* Annie Sullivan <annie.sullivan@gmail.com> (original author)
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIURI;
|
||||
|
||||
/**
|
||||
* The Bookmarks Container interface provides a base class
|
||||
* for services that want to provide containers for bookmarks.
|
||||
* Some examples of possible services are the livemarks service and
|
||||
* the filesystem. Containers are implemented as bookmarked folders.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(45bf2020-9683-498c-9638-f08130c4151d)]
|
||||
interface nsIBookmarksContainer : nsISupports
|
||||
{
|
||||
|
||||
/**
|
||||
* Called when the given container is about to be shown,
|
||||
* so that the service can populate the container if necessary.
|
||||
* @returns true if the data for the node has changed, false otherwise.
|
||||
* @param container The folderId of the bookmark folder
|
||||
* representing the container.
|
||||
*/
|
||||
boolean onContainerOpening(in PRInt64 container);
|
||||
|
||||
/**
|
||||
* Called when the given container has just been hidden,
|
||||
* so that the service can do any necessary cleanup.
|
||||
* @param container The folderId of the bookmark folder
|
||||
* representing the container.
|
||||
*/
|
||||
void onContainerClosed(in PRInt64 container);
|
||||
|
||||
/**
|
||||
* Called when the given container is about to be deleted, so
|
||||
* that the service can do any necessary cleanup.
|
||||
* Called BEFORE the container is deleted, so that the service
|
||||
* can still reference it.
|
||||
* @param container The folderId of the bookmark folder
|
||||
* representing the container to be deleted.
|
||||
*/
|
||||
void onContainerRemoving(in PRInt64 container);
|
||||
|
||||
/**
|
||||
* Called when the given container has just been moved, in case
|
||||
* the service needs to do any bookkeeping.
|
||||
* Called AFTER the container has been moved, so the service can
|
||||
* get the new URI.
|
||||
* @param container The folderId of the bookmark folder
|
||||
* representing the container to be moved.
|
||||
* @param newFolder The folderId of the new parent folder
|
||||
* for the container.
|
||||
* @param newIndex The index the container will be inserted at,
|
||||
* or -1 for append.
|
||||
*/
|
||||
void onContainerMoved(in PRInt64 container,
|
||||
in PRInt64 newFolder, in PRInt32 newIndex);
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: C++; tab-width: 2; 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 Places code
|
||||
*
|
||||
* 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):
|
||||
* Annie Sullivan <annie.sullivan@gmail.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 ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIBookmarksContainer.idl"
|
||||
|
||||
interface nsIURI;
|
||||
|
||||
[scriptable, uuid(b1257934-86cf-4143-8386-734ac352b6ba)]
|
||||
interface nsILivemarkService : nsIBookmarksContainer
|
||||
{
|
||||
/**
|
||||
* Creates a new livemark
|
||||
* @param folder The id of the parent folder
|
||||
* @param name The name to show when displaying the livemark
|
||||
* @param siteURI The URI of the site the livemark was created from
|
||||
* @param feedURI The URI of the actual RSS feed
|
||||
* @param index The index to insert at, or -1 to append
|
||||
* @returns the ID of the folder for the livemark
|
||||
*/
|
||||
PRInt64 createLivemark(in PRInt64 folder,
|
||||
in AString name,
|
||||
in nsIURI siteURI,
|
||||
in nsIURI feedURI,
|
||||
in PRInt32 index);
|
||||
};
|
|
@ -223,12 +223,31 @@ interface nsINavBookmarksService : nsISupports
|
|||
*/
|
||||
PRInt64 createFolder(in PRInt64 parent, in AString name, in PRInt32 index);
|
||||
|
||||
/**
|
||||
* Wrapper for container services. Creates a folder under the given
|
||||
* parent and sets the container type.
|
||||
* @param parent The id of the parent folder
|
||||
* @param name The name of the new folder
|
||||
* @param index The index to insert at, or -1 to append
|
||||
* @param type The type of container to insert
|
||||
* @returns the ID of the newly-inserted folder
|
||||
*/
|
||||
PRInt64 createContainer(in PRInt64 parent, in AString name,
|
||||
in PRInt32 index, in AString type);
|
||||
|
||||
/**
|
||||
* Removes a folder from the bookmarks tree.
|
||||
* @param folder The id of the folder to remove.
|
||||
*/
|
||||
void removeFolder(in PRInt64 folder);
|
||||
|
||||
/**
|
||||
* Convenience function for container services. Removes
|
||||
* all children of the given folder.
|
||||
* @param folder The id of the folder to remove children from.
|
||||
*/
|
||||
void removeFolderChildren(in PRInt64 folder);
|
||||
|
||||
/**
|
||||
* Moves a folder to a different container, preserving its contents.
|
||||
* @param folder The folder to move
|
||||
|
@ -275,6 +294,13 @@ interface nsINavBookmarksService : nsISupports
|
|||
*/
|
||||
AString getFolderTitle(in PRInt64 folder);
|
||||
|
||||
/**
|
||||
* Get the place: url for a bookmark folder.
|
||||
* @param folder The folder whose URI should be retrieved
|
||||
* @returns The URI for the folder
|
||||
*/
|
||||
nsIURI getFolderURI(in PRInt64 folder);
|
||||
|
||||
/**
|
||||
* Marks a folder as having read-only children.
|
||||
* If this is set to true, UI should not allow the user to add, remove,
|
||||
|
|
|
@ -77,6 +77,13 @@ interface nsINavHistoryResultNode : nsISupports
|
|||
*/
|
||||
readonly attribute PRInt64 folderId;
|
||||
|
||||
/**
|
||||
* Type of the folder corresponding to this node.
|
||||
* Only valid for RESULT_TYPE_QUERY nodes where exactly one folder
|
||||
* has been specified in the query. 0 in all other cases.
|
||||
*/
|
||||
readonly attribute AString folderType;
|
||||
|
||||
/**
|
||||
* Get the queries which build this node's children.
|
||||
* Only valid for RESULT_TYPE_QUERY nodes.
|
||||
|
|
|
@ -77,6 +77,13 @@ interface nsINavHistoryResultNode : nsISupports
|
|||
*/
|
||||
readonly attribute PRInt64 folderId;
|
||||
|
||||
/**
|
||||
* Type of the folder corresponding to this node.
|
||||
* Only valid for RESULT_TYPE_QUERY nodes where exactly one folder
|
||||
* has been specified in the query. 0 in all other cases.
|
||||
*/
|
||||
readonly attribute AString folderType;
|
||||
|
||||
/**
|
||||
* Get the queries which build this node's children.
|
||||
* Only valid for RESULT_TYPE_QUERY nodes.
|
||||
|
|
|
@ -67,6 +67,10 @@ REQUIRES = xpcom \
|
|||
htmlparser \
|
||||
content \
|
||||
txmgr \
|
||||
xmlextras \
|
||||
caps \
|
||||
xpconnect \
|
||||
js \
|
||||
nkcache \
|
||||
$(NULL)
|
||||
|
||||
|
@ -81,6 +85,8 @@ CPPSRCS = \
|
|||
nsNavHistoryQuery.cpp \
|
||||
nsNavHistoryResult.cpp \
|
||||
nsNavBookmarks.cpp \
|
||||
nsLivemarkService.cpp \
|
||||
nsBookmarksFeedHandler.cpp \
|
||||
nsMaybeWeakPtr.cpp \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,806 @@
|
|||
/* -*- Mode: C++; tab-width: 2; 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 mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Vladimir Vukicevic <vladimir@pobox.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2004
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Annie Sullivan <annie.sullivan@gmail.com> (modified to work with places)
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
// THE ORIGINAL LOCATION OF THIS CODE IS
|
||||
// /browser/components/bookmarks/src/nsBookmarksFeedHandler.cpp
|
||||
|
||||
#include "nsLivemarkService.h"
|
||||
#include "nsArrayEnumerator.h"
|
||||
#include "nsArray.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIRDFContainer.h"
|
||||
#include "nsIRDFContainerUtils.h"
|
||||
#include "nsIRDFService.h"
|
||||
#include "nsIRDFXMLSerializer.h"
|
||||
#include "nsIRDFXMLSource.h"
|
||||
#include "nsIRDFXMLParser.h"
|
||||
#include "nsRDFCID.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "rdf.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsInt64.h"
|
||||
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
|
||||
#include "nsIURL.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsICachingChannel.h"
|
||||
#include "nsICacheVisitor.h"
|
||||
|
||||
#include "nsIDOMParser.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMElement.h"
|
||||
#include "nsIDOMCharacterData.h"
|
||||
#include "nsIDOMNodeList.h"
|
||||
|
||||
// These are defined in nsLivemarkService.cpp
|
||||
extern nsIRDFResource *kLMRDF_type;
|
||||
|
||||
extern nsIRDFResource *kLMRSS09_channel;
|
||||
extern nsIRDFResource *kLMRSS09_item;
|
||||
extern nsIRDFResource *kLMRSS09_title;
|
||||
extern nsIRDFResource *kLMRSS09_link;
|
||||
|
||||
extern nsIRDFResource *kLMRSS10_channel;
|
||||
extern nsIRDFResource *kLMRSS10_items;
|
||||
extern nsIRDFResource *kLMRSS10_title;
|
||||
extern nsIRDFResource *kLMRSS10_link;
|
||||
|
||||
extern nsIRDFResource *kLMDC_date;
|
||||
|
||||
static NS_DEFINE_CID(kRDFContainerCID, NS_RDFCONTAINER_CID);
|
||||
static NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// nsLivemarkLoadListener
|
||||
//
|
||||
// An nsIStreamListener implementation that UpdateLivemarkChildren uses
|
||||
// to aysnchronously fetch and update a livemark's child entries.
|
||||
//
|
||||
// This could potentially become a nested class within the livemarks service.
|
||||
//
|
||||
|
||||
class nsLivemarkLoadListener : public nsIStreamListener
|
||||
{
|
||||
public:
|
||||
nsLivemarkLoadListener(nsLivemarkService *aLivemarkService,
|
||||
nsCOMPtr<nsIAnnotationService> aAnnotationService,
|
||||
nsLivemarkService::LivemarkInfo * pLivemark)
|
||||
: mLivemarkService(aLivemarkService), mAnnotationService(aAnnotationService),
|
||||
mLivemark(pLivemark), mAborted(PR_FALSE)
|
||||
{
|
||||
NS_IF_ADDREF(mLivemarkService);
|
||||
}
|
||||
|
||||
virtual ~nsLivemarkLoadListener() {
|
||||
NS_IF_RELEASE(mLivemarkService);
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
|
||||
void Abort () { mAborted = PR_TRUE; }
|
||||
|
||||
protected:
|
||||
static NS_METHOD StreamReaderCallback(nsIInputStream *in,
|
||||
void *closure,
|
||||
const char *fromRawSegment,
|
||||
PRUint32 toOffset,
|
||||
PRUint32 count,
|
||||
PRUint32 *writeCount);
|
||||
|
||||
NS_METHOD TryParseAsRDF();
|
||||
NS_METHOD TryParseAsSimpleRSS();
|
||||
NS_METHOD SetResourceTTL(PRInt32 ttl);
|
||||
|
||||
// helpers
|
||||
NS_METHOD HandleRDFItem (nsIRDFDataSource *aDS, nsIRDFResource *itemResource,
|
||||
nsIRDFResource *aLinkResource, nsIRDFResource *aTitleResource);
|
||||
NS_METHOD FindTextInNode (nsIDOMNode *aParentNode, nsAString &aString);
|
||||
|
||||
PRBool IsLinkValid(const PRUnichar *aURI);
|
||||
|
||||
nsLivemarkService *mLivemarkService;
|
||||
nsCOMPtr<nsIAnnotationService> mAnnotationService;
|
||||
nsLivemarkService::LivemarkInfo *mLivemark;
|
||||
nsCOMPtr<nsIOutputStream> mCacheStream;
|
||||
nsCOMPtr<nsIScriptSecurityManager> mSecMan;
|
||||
PRBool mAborted;
|
||||
nsCString mBody;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsLivemarkLoadListener, nsIStreamListener, nsIRequestObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLivemarkLoadListener::OnStartRequest(nsIRequest *aResult, nsISupports *ctxt)
|
||||
{
|
||||
if (mAborted)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
mBody.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_METHOD
|
||||
nsLivemarkLoadListener::StreamReaderCallback(nsIInputStream *aInputStream,
|
||||
void *aClosure,
|
||||
const char *aRawSegment,
|
||||
PRUint32 aToOffset,
|
||||
PRUint32 aCount,
|
||||
PRUint32 *aWriteCount)
|
||||
{
|
||||
nsLivemarkLoadListener *rll = (nsLivemarkLoadListener *) aClosure;
|
||||
|
||||
rll->mBody.Append(aRawSegment, aCount);
|
||||
*aWriteCount = aCount;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLivemarkLoadListener::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsIInputStream *aInputStream,
|
||||
PRUint32 aSourceOffset,
|
||||
PRUint32 aCount)
|
||||
{
|
||||
PRUint32 totalRead;
|
||||
return aInputStream->ReadSegments(StreamReaderCallback, (void *)this, aCount, &totalRead);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLivemarkLoadListener::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatus)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (mAborted) {
|
||||
mLivemark->locked = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_FAILED(aStatus)) {
|
||||
// Something went wrong; try to load again in 5 minutes.
|
||||
SetResourceTTL (300);
|
||||
mLivemark->locked = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Clear out any child nodes of the livemark folder, since
|
||||
// they're about to be replaced.
|
||||
rv = mLivemarkService->DeleteLivemarkChildren(mLivemark->folderId);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
mLivemark->locked = PR_FALSE;
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Grab the security manager
|
||||
mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
|
||||
// We need to parse the returned data here, stored in mBody. We
|
||||
// try parsing as RDF first, then as Atom and the "simple" RSS
|
||||
// (the userland 0.91/0.92/2.0 formats)
|
||||
//
|
||||
|
||||
// Try parsing as RDF
|
||||
rv = TryParseAsRDF();
|
||||
|
||||
// Try parsing as Atom/Simple RSS
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
rv = TryParseAsSimpleRSS();
|
||||
}
|
||||
|
||||
// If we weren't able to load with anything, attach a dummy bookmark
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
rv = mLivemarkService->InsertLivemarkFailedItem(mLivemark->folderId);
|
||||
}
|
||||
|
||||
// Set an expiration on the livemark, for reloading the data
|
||||
PRInt32 ttl;
|
||||
if (NS_FAILED(rv)) {
|
||||
// if we failed, try again in 1 hour, to avoid trying
|
||||
// to load a feed that doesn't parse over and over.
|
||||
ttl = 3600;
|
||||
} else {
|
||||
// TODO -- read the pref for livemark reload time and set to max of
|
||||
// the pref value and 1 minute.
|
||||
ttl = 3600; // 1 hr default
|
||||
|
||||
// ensure that the ttl is at least equal to the cache expiry time, since
|
||||
// otherwise a reload won't have much effect
|
||||
nsCOMPtr<nsICachingChannel> channel = do_QueryInterface(aRequest);
|
||||
if (channel) {
|
||||
nsCOMPtr<nsISupports> cacheToken;
|
||||
channel->GetCacheToken(getter_AddRefs(cacheToken));
|
||||
if (cacheToken) {
|
||||
nsCOMPtr<nsICacheEntryInfo> entryInfo = do_QueryInterface(cacheToken);
|
||||
if (entryInfo) {
|
||||
PRUint32 expiresTime;
|
||||
|
||||
if (NS_SUCCEEDED(entryInfo->GetExpirationTime(&expiresTime))) {
|
||||
PRInt64 temp64, nowtime = PR_Now();
|
||||
PRUint32 nowsec;
|
||||
LL_I2L(temp64, PR_USEC_PER_SEC);
|
||||
LL_DIV(temp64, nowtime, temp64);
|
||||
LL_L2UI(nowsec, temp64);
|
||||
|
||||
if (nowsec >= expiresTime) {
|
||||
expiresTime -= nowsec;
|
||||
if (ttl < (PRInt32) expiresTime)
|
||||
ttl = (PRInt32) expiresTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rv = SetResourceTTL(ttl);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING ("SetResourceTTL failed on Livemark");
|
||||
}
|
||||
|
||||
mLivemark->locked = PR_FALSE;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* SetResourceTTL: Set the next time we should attempt to reload this
|
||||
* resource's feed
|
||||
*/
|
||||
|
||||
nsresult
|
||||
nsLivemarkLoadListener::SetResourceTTL (PRInt32 aTTL)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
PRTime million, temp64, exptime = PR_Now();
|
||||
LL_I2L (million, PR_USEC_PER_SEC);
|
||||
LL_I2L (temp64, aTTL);
|
||||
LL_MUL (temp64, temp64, million);
|
||||
LL_ADD (exptime, exptime, temp64);
|
||||
|
||||
|
||||
rv = mAnnotationService->SetAnnotationInt64(mLivemark->feedURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_EXPIRATION),
|
||||
exptime, 0,
|
||||
nsIAnnotationService::EXPIRE_NEVER);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* TryParseAsRDF: attempt to parse the read data in mBody as
|
||||
* RSS 0.90/1.0 data. This is supposed to be RDF, so we use
|
||||
* the RDF parser to do our work for us.
|
||||
*/
|
||||
|
||||
nsresult
|
||||
nsLivemarkLoadListener::TryParseAsRDF ()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIRDFXMLParser> rdfparser(do_CreateInstance("@mozilla.org/rdf/xml-parser;1", &rv));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIRDFDataSource> ds(do_CreateInstance(kRDFInMemoryDataSourceCID, &rv));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = rdfparser->ParseString (ds, mLivemark->feedURI, mBody);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// Grab the (only) thing that's a channel
|
||||
// We try RSS 1.0 first, then RSS 0.9, and set up the remaining
|
||||
// resources accordingly
|
||||
|
||||
nsIRDFResource *RSS_items = nsnull;
|
||||
nsIRDFResource *RSS_title = nsnull;
|
||||
nsIRDFResource *RSS_link = nsnull;
|
||||
|
||||
nsCOMPtr<nsIRDFResource> channelResource = nsnull;
|
||||
|
||||
rv = ds->GetSource(kLMRDF_type, kLMRSS10_channel, PR_TRUE, getter_AddRefs(channelResource));
|
||||
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
|
||||
if (rv == NS_OK) {
|
||||
RSS_items = kLMRSS10_items;
|
||||
RSS_title = kLMRSS10_title;
|
||||
RSS_link = kLMRSS10_link;
|
||||
} else {
|
||||
// try RSS 0.9
|
||||
rv = ds->GetSource(kLMRDF_type, kLMRSS09_channel, PR_TRUE, getter_AddRefs(channelResource));
|
||||
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
|
||||
if (rv == NS_OK) {
|
||||
RSS_items = nsnull;
|
||||
RSS_title = kLMRSS09_title;
|
||||
RSS_link = kLMRSS09_link;
|
||||
}
|
||||
}
|
||||
|
||||
if (!channelResource) {
|
||||
// no channel, either 1.0 or 0.9
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// this will get filled in differently.
|
||||
nsCOMPtr<nsISimpleEnumerator> itemsEnumerator;
|
||||
|
||||
if (RSS_items) {
|
||||
// if there is something that should be rss:items, then it's RSS 1.0
|
||||
nsCOMPtr<nsIRDFNode> itemsNode;
|
||||
rv = ds->GetTarget(channelResource, RSS_items, PR_TRUE, getter_AddRefs(itemsNode));
|
||||
if (NS_FAILED(rv) || rv == NS_RDF_NO_VALUE) return NS_ERROR_FAILURE;
|
||||
|
||||
// items is a seq
|
||||
nsCOMPtr<nsIRDFContainer> itemsContainer = do_CreateInstance (kRDFContainerCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = itemsContainer->Init(ds, (nsIRDFResource *) itemsNode.get());
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
rv = itemsContainer->GetElements (getter_AddRefs(itemsEnumerator));
|
||||
if (NS_FAILED(rv) || rv == NS_RDF_NO_VALUE) return NS_ERROR_FAILURE;
|
||||
} else {
|
||||
//
|
||||
// if there is no rss:items, but we still were able to parse it as RDF
|
||||
// and found a channel, then it's possibly RSS 0.9. For RSS 0.9,
|
||||
// we know that each item will be an <item ...>, so we get everything
|
||||
// that has a type of item.
|
||||
rv = ds->GetSources(kLMRDF_type, kLMRSS09_item, PR_TRUE, getter_AddRefs(itemsEnumerator));
|
||||
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Go through each resource and pull out its link/title, if present.
|
||||
PRBool more;
|
||||
while (NS_SUCCEEDED(rv = itemsEnumerator->HasMoreElements(&more)) && more) {
|
||||
nsCOMPtr<nsISupports> iSupports;
|
||||
rv = itemsEnumerator->GetNext(getter_AddRefs(iSupports));
|
||||
if (NS_FAILED(rv)) break;
|
||||
|
||||
nsCOMPtr<nsIRDFResource> item(do_QueryInterface(iSupports));
|
||||
if (!item) {
|
||||
rv = NS_ERROR_UNEXPECTED;
|
||||
break;
|
||||
}
|
||||
|
||||
rv = HandleRDFItem (ds, item, RSS_link, RSS_title);
|
||||
// ignore rv
|
||||
}
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// find the first node below aParentNode that is a TEXT_NODE,
|
||||
// and return it
|
||||
//
|
||||
nsresult
|
||||
nsLivemarkLoadListener::FindTextInNode (nsIDOMNode *aParentNode, nsAString &aString)
|
||||
{
|
||||
NS_ENSURE_ARG(aParentNode);
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> childNode;
|
||||
rv = aParentNode->GetFirstChild(getter_AddRefs(childNode));
|
||||
if (!childNode) return NS_OK;
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
PRUint16 nodeType = 0;
|
||||
do {
|
||||
rv = childNode->GetNodeType(&nodeType);
|
||||
if (nodeType == nsIDOMNode::TEXT_NODE ||
|
||||
nodeType == nsIDOMNode::CDATA_SECTION_NODE)
|
||||
break;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> temp;
|
||||
rv = childNode->GetNextSibling(getter_AddRefs(temp));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
childNode = temp;
|
||||
} while (childNode);
|
||||
|
||||
if (nodeType == nsIDOMNode::TEXT_NODE ||
|
||||
nodeType == nsIDOMNode::CDATA_SECTION_NODE)
|
||||
{
|
||||
nsCOMPtr<nsIDOMCharacterData> charTextNode = do_QueryInterface(childNode);
|
||||
if (charTextNode)
|
||||
return charTextNode->GetData(aString);
|
||||
}
|
||||
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsLivemarkLoadListener::HandleRDFItem (nsIRDFDataSource *aDS, nsIRDFResource *aItem,
|
||||
nsIRDFResource *aLinkResource,
|
||||
nsIRDFResource *aTitleResource)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// We care about this item's link and title
|
||||
nsCOMPtr<nsIRDFNode> linkNode;
|
||||
rv = aDS->GetTarget (aItem, aLinkResource, PR_TRUE, getter_AddRefs(linkNode));
|
||||
if (NS_FAILED(rv) || rv == NS_RDF_NO_VALUE) return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIRDFNode> titleNode;
|
||||
rv = aDS->GetTarget (aItem, aTitleResource, PR_TRUE, getter_AddRefs(titleNode));
|
||||
if (rv == NS_RDF_NO_VALUE) {
|
||||
rv = aDS->GetTarget (aItem, kLMDC_date, PR_TRUE, getter_AddRefs(titleNode));
|
||||
}
|
||||
if (NS_FAILED(rv) || rv == NS_RDF_NO_VALUE) return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIRDFLiteral> linkLiteral(do_QueryInterface(linkNode));
|
||||
nsCOMPtr<nsIRDFLiteral> titleLiteral(do_QueryInterface(titleNode));
|
||||
|
||||
// if the link/title points to something other than a literal skip it.
|
||||
if (!linkLiteral || !titleLiteral)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
const PRUnichar *linkStr, *titleStr;
|
||||
rv = linkLiteral->GetValueConst(&linkStr);
|
||||
rv |= titleLiteral->GetValueConst(&titleStr);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (!IsLinkValid(linkStr))
|
||||
return NS_OK;
|
||||
|
||||
// Add new bookmark with the link and title.
|
||||
nsCOMPtr<nsIURI> linkURI;
|
||||
rv = NS_NewURI(getter_AddRefs(linkURI), NS_ConvertUTF16toUTF8(linkStr));
|
||||
rv = mLivemarkService->InsertLivemarkChild(mLivemark->folderId,
|
||||
linkURI,
|
||||
nsDependentString(titleStr));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* TryParseAsSimpleRSS
|
||||
*
|
||||
* Tries to parse the content as RSS (Userland) 0.91/0.92/2.0, or Atom
|
||||
* These are not RDF formats.
|
||||
*/
|
||||
|
||||
nsresult
|
||||
nsLivemarkLoadListener::TryParseAsSimpleRSS ()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIDOMParser> parser(do_CreateInstance("@mozilla.org/xmlextras/domparser;1", &rv));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> xmldoc;
|
||||
rv = parser->ParseFromBuffer ((const PRUint8*) mBody.get(), mBody.Length(), "text/xml", getter_AddRefs(xmldoc));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// becomes true if we figure out that this is an atom stream.
|
||||
PRBool isAtom = PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIDOMElement> docElement;
|
||||
rv = xmldoc->GetDocumentElement(getter_AddRefs(docElement));
|
||||
if (!docElement) return NS_ERROR_UNEXPECTED;
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> node;
|
||||
rv = xmldoc->GetFirstChild(getter_AddRefs(node));
|
||||
if (!node) return NS_ERROR_UNEXPECTED;
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
PRBool lookingForChannel = PR_FALSE;
|
||||
|
||||
while (node) {
|
||||
PRUint16 ntype;
|
||||
rv = node->GetNodeType(&ntype);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (ntype == nsIDOMNode::ELEMENT_NODE) {
|
||||
nsAutoString nname;
|
||||
rv = node->GetNodeName (nname);
|
||||
|
||||
// slight hack to get node pointing to the thing that
|
||||
// has items/entries as its children. We need to descend
|
||||
// into <channel> for RSS, but not for Atom.
|
||||
if (!lookingForChannel) {
|
||||
if (nname.Equals(NS_LITERAL_STRING("rss"))) {
|
||||
lookingForChannel = PR_TRUE;
|
||||
nsCOMPtr<nsIDOMNode> temp;
|
||||
rv = node->GetFirstChild(getter_AddRefs(temp));
|
||||
if (!temp) return NS_ERROR_UNEXPECTED;
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
node = temp;
|
||||
continue;
|
||||
}
|
||||
if (nname.Equals(NS_LITERAL_STRING("feed"))) {
|
||||
// Atom has no <channel>; instead, <item>s are
|
||||
// children of <feed>
|
||||
isAtom = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (nname.Equals(NS_LITERAL_STRING("channel"))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNode> temp;
|
||||
rv = node->GetNextSibling(getter_AddRefs(temp));
|
||||
if (!temp) return NS_ERROR_UNEXPECTED;
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
node = temp;
|
||||
}
|
||||
|
||||
// we didn't find a rss/feed/channel or whatever
|
||||
if (!node)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIDOMElement> chElement = do_QueryInterface(node);
|
||||
if (!chElement) return NS_ERROR_UNEXPECTED;
|
||||
|
||||
// Go through the <channel>/<feed> and do what we need
|
||||
// with <item> or <entry> nodes
|
||||
int numMarksAdded = 0;
|
||||
|
||||
rv = chElement->GetFirstChild(getter_AddRefs(node));
|
||||
if (!node) return NS_ERROR_UNEXPECTED;
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
while (node) {
|
||||
PRUint16 ntype;
|
||||
rv = node->GetNodeType(&ntype);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (ntype == nsIDOMNode::ELEMENT_NODE) {
|
||||
nsAutoString nname;
|
||||
rv = node->GetNodeName (nname);
|
||||
|
||||
if ((!isAtom && nname.Equals(NS_LITERAL_STRING("item"))) ||
|
||||
( isAtom && nname.Equals(NS_LITERAL_STRING("entry"))))
|
||||
{
|
||||
// We need to pull out the <title> and <link> children
|
||||
nsAutoString titleStr;
|
||||
nsAutoString linkStr;
|
||||
nsAutoString dateStr;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> childNode;
|
||||
rv = node->GetFirstChild(getter_AddRefs(childNode));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
while (childNode) {
|
||||
PRUint16 childNtype;
|
||||
rv = childNode->GetNodeType(&childNtype);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (childNtype == nsIDOMNode::ELEMENT_NODE) {
|
||||
nsAutoString childNname;
|
||||
rv = childNode->GetNodeName (childNname);
|
||||
|
||||
if (childNname.Equals(NS_LITERAL_STRING("title"))) {
|
||||
rv = FindTextInNode (childNode, titleStr);
|
||||
if (NS_FAILED(rv)) break;
|
||||
} else if (childNname.Equals(NS_LITERAL_STRING("pubDate")) ||
|
||||
childNname.Equals(NS_LITERAL_STRING("updated")))
|
||||
{
|
||||
rv = FindTextInNode (childNode, dateStr);
|
||||
if (NS_FAILED(rv)) break;
|
||||
} else if (!isAtom && childNname.Equals(NS_LITERAL_STRING("guid"))) {
|
||||
nsCOMPtr<nsIDOMElement> linkElem = do_QueryInterface(childNode);
|
||||
if (!linkElem) break; // out of while(childNode) loop
|
||||
|
||||
nsAutoString isPermaLink;
|
||||
linkElem->GetAttribute(NS_LITERAL_STRING("isPermaLink"), isPermaLink);
|
||||
// Ignore failures. isPermaLink defaults to true.
|
||||
if (!isPermaLink.Equals(NS_LITERAL_STRING("false"))) {
|
||||
// in node's TEXT
|
||||
rv = FindTextInNode (childNode, linkStr);
|
||||
if (NS_FAILED(rv)) break;
|
||||
}
|
||||
} else if (childNname.Equals(NS_LITERAL_STRING("link"))) {
|
||||
if (isAtom) {
|
||||
// in HREF attribute
|
||||
nsCOMPtr<nsIDOMElement> linkElem = do_QueryInterface(childNode);
|
||||
if (!linkElem) break; // out of while(childNode) loop
|
||||
|
||||
nsAutoString rel;
|
||||
linkElem->GetAttribute(NS_LITERAL_STRING("rel"), rel);
|
||||
if (rel.Equals(NS_LITERAL_STRING("alternate")) ||
|
||||
rel.IsEmpty())
|
||||
{
|
||||
rv = linkElem->GetAttribute(NS_LITERAL_STRING("href"), linkStr);
|
||||
if (NS_FAILED(rv)) break; // out of while(childNode) loop
|
||||
}
|
||||
} else if (linkStr.IsEmpty()) {
|
||||
// in node's TEXT
|
||||
rv = FindTextInNode (childNode, linkStr);
|
||||
if (NS_FAILED(rv)) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!titleStr.IsEmpty() && !linkStr.IsEmpty())
|
||||
break;
|
||||
|
||||
nsCOMPtr<nsIDOMNode> temp;
|
||||
rv = childNode->GetNextSibling(getter_AddRefs(temp));
|
||||
childNode = temp;
|
||||
if (!childNode || NS_FAILED(rv)) break;
|
||||
}
|
||||
|
||||
if (titleStr.IsEmpty() && !dateStr.IsEmpty())
|
||||
titleStr.Assign(dateStr);
|
||||
|
||||
if (!titleStr.IsEmpty() && !linkStr.IsEmpty() && IsLinkValid(linkStr.get())) {
|
||||
nsCOMPtr<nsIURI> linkURI;
|
||||
rv = NS_NewURI(getter_AddRefs(linkURI),
|
||||
NS_ConvertUTF16toUTF8(linkStr));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = mLivemarkService->InsertLivemarkChild(mLivemark->folderId,
|
||||
linkURI,
|
||||
nsDependentString(titleStr));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
numMarksAdded++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMNode> temp;
|
||||
rv = node->GetNextSibling(getter_AddRefs(temp));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
node = temp;
|
||||
}
|
||||
|
||||
|
||||
if (numMarksAdded > 0) {
|
||||
// if no items were found, return NS_ERROR_FAILURE so that
|
||||
// the RDF parser can be tried.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// return NS_OK if items were found so that the RDF parser isn't tried.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// return true if this link is valid and a livemark should be created;
|
||||
// otherwise, false.
|
||||
PRBool
|
||||
nsLivemarkLoadListener::IsLinkValid(const PRUnichar *aURI)
|
||||
{
|
||||
nsCOMPtr<nsIURI> linkuri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(linkuri), nsDependentString(aURI));
|
||||
if (NS_FAILED(rv))
|
||||
return PR_FALSE;
|
||||
|
||||
// Er, where'd our security manager go?
|
||||
if (!mSecMan)
|
||||
return PR_FALSE;
|
||||
|
||||
rv = mSecMan->CheckLoadURI(mLivemark->feedURI, linkuri,
|
||||
nsIScriptSecurityManager::DISALLOW_FROM_MAIL |
|
||||
nsIScriptSecurityManager::DISALLOW_SCRIPT_OR_DATA);
|
||||
if (NS_FAILED(rv))
|
||||
return PR_FALSE;
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
//// Main entry point for nsLivemarkService to deal with Livemark
|
||||
//// load listener
|
||||
////
|
||||
|
||||
/*
|
||||
* Update the child elements of a livemark; take care of cache checking,
|
||||
* channel setup and firing off the async load and parse.
|
||||
*/
|
||||
nsresult
|
||||
nsLivemarkService::UpdateLivemarkChildren(PRInt32 aLivemarkIndex)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (mLivemarks[aLivemarkIndex].locked)
|
||||
{
|
||||
// We're already loading the livemark
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Lock the livemark
|
||||
mLivemarks[aLivemarkIndex].locked = PR_TRUE;
|
||||
|
||||
// Check the TTL/expiration on this. If there isn't one,
|
||||
// then we assume it's never been loaded.
|
||||
PRTime exprTime;
|
||||
rv = mAnnotationService->GetAnnotationInt64(mLivemarks[aLivemarkIndex].feedURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_EXPIRATION),
|
||||
&exprTime);
|
||||
if (rv == NS_OK) {
|
||||
PRTime nowTime = PR_Now();
|
||||
if (LL_CMP(exprTime, >, nowTime)) {
|
||||
// No need to refresh yet.
|
||||
mLivemarks[aLivemarkIndex].locked = PR_FALSE;
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
// This livemark has never been loaded, since it has no expire time.
|
||||
rv = InsertLivemarkLoadingItem(mLivemarks[aLivemarkIndex].folderId);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> uriChannel;
|
||||
rv = NS_NewChannel(getter_AddRefs(uriChannel),
|
||||
mLivemarks[aLivemarkIndex].feedURI,
|
||||
nsnull, nsnull, nsnull,
|
||||
nsIRequest::LOAD_BACKGROUND);
|
||||
if (NS_FAILED(rv)) {
|
||||
mLivemarks[aLivemarkIndex].locked = PR_FALSE;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(uriChannel);
|
||||
if (httpChannel) {
|
||||
// XXXvladimir - handle POST livemarks
|
||||
rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET"));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsLivemarkLoadListener> listener = new nsLivemarkLoadListener(this,
|
||||
mAnnotationService,
|
||||
&mLivemarks[aLivemarkIndex]);
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel),
|
||||
mLivemarks[aLivemarkIndex].feedURI, nsnull, nsnull, nsnull,
|
||||
nsIRequest::LOAD_BACKGROUND | nsIRequest::VALIDATE_ALWAYS);
|
||||
if (NS_FAILED(rv)) {
|
||||
mLivemarks[aLivemarkIndex].locked = PR_FALSE;
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = channel->AsyncOpen(listener, nsnull);
|
||||
if (NS_FAILED(rv)) {
|
||||
mLivemarks[aLivemarkIndex].locked = PR_FALSE;
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
|
@ -0,0 +1,468 @@
|
|||
/* -*- Mode: C++; tab-width: 2; 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 Places.
|
||||
*
|
||||
* 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):
|
||||
* Annie Sullivan <annie.sullivan@gmail.com> (original author)
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#include "nsLivemarkService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsNavBookmarks.h"
|
||||
#include "nsIAnnotationService.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "rdf.h"
|
||||
#include "nsIRDFService.h"
|
||||
#include "nsRDFCID.h"
|
||||
|
||||
#define LIVEMARK_TIMEOUT 15000 // fire every 15 seconds
|
||||
#define PLACES_STRING_BUNDLE_URI "chrome://browser/locale/places/places.properties"
|
||||
|
||||
// Constants for parsing RDF Livemarks
|
||||
// These are used in nsBookmarksFeedHandler.cpp, but because there is
|
||||
// no initialization function in the nsLivemarkLoadListener class, they
|
||||
// are initialized by the nsLivemarkService::Init() function in this file.
|
||||
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
|
||||
#ifndef RSS09_NAMESPACE_URI
|
||||
#define RSS09_NAMESPACE_URI "http://my.netscape.com/rdf/simple/0.9/"
|
||||
#endif
|
||||
#ifndef RSS10_NAMESPACE_URI
|
||||
#define RSS10_NAMESPACE_URI "http://purl.org/rss/1.0/"
|
||||
#endif
|
||||
#ifndef DC_NAMESPACE_URI
|
||||
#define DC_NAMESPACE_URI "http://purl.org/dc/elements/1.1/"
|
||||
#endif
|
||||
nsIRDFResource *kLMRDF_type;
|
||||
nsIRDFResource *kLMRSS09_channel;
|
||||
nsIRDFResource *kLMRSS09_item;
|
||||
nsIRDFResource *kLMRSS09_title;
|
||||
nsIRDFResource *kLMRSS09_link;
|
||||
nsIRDFResource *kLMRSS10_channel;
|
||||
nsIRDFResource *kLMRSS10_items;
|
||||
nsIRDFResource *kLMRSS10_title;
|
||||
nsIRDFResource *kLMRSS10_link;
|
||||
nsIRDFResource *kLMDC_date;
|
||||
|
||||
nsLivemarkService* nsLivemarkService::sInstance = nsnull;
|
||||
|
||||
|
||||
nsLivemarkService::nsLivemarkService()
|
||||
: mTimer(nsnull)
|
||||
{
|
||||
NS_ASSERTION(!sInstance, "Multiple nsLivemarkService instances!");
|
||||
sInstance = this;
|
||||
}
|
||||
|
||||
nsLivemarkService::~nsLivemarkService()
|
||||
{
|
||||
NS_ASSERTION(sInstance == this, "Expected sInstance == this");
|
||||
sInstance = nsnull;
|
||||
if (mTimer)
|
||||
{
|
||||
// be sure to cancel the timer, as it holds a
|
||||
// weak reference back to nsLivemarkService
|
||||
mTimer->Cancel();
|
||||
mTimer = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsLivemarkService,
|
||||
nsILivemarkService,
|
||||
nsIBookmarksContainer)
|
||||
|
||||
nsresult
|
||||
nsLivemarkService::Init()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Get string bundle for livemark messages
|
||||
nsCOMPtr<nsIStringBundleService> bundleService =
|
||||
do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = bundleService->CreateBundle(
|
||||
PLACES_STRING_BUNDLE_URI,
|
||||
getter_AddRefs(mBundle));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Initialize the localized strings for the names of the dummy
|
||||
// "livemark loading..." and "livemark failed to load" bookmarks
|
||||
rv = mBundle->GetStringFromName(NS_LITERAL_STRING("bookmarksLivemarkLoading").get(),
|
||||
getter_Copies(mLivemarkLoading));
|
||||
if (NS_FAILED(rv)) {
|
||||
mLivemarkLoading.Assign(NS_LITERAL_STRING("Live Bookmark loading..."));
|
||||
}
|
||||
|
||||
nsXPIDLString lmfailedName;
|
||||
rv = mBundle->GetStringFromName(NS_LITERAL_STRING("bookmarksLivemarkFailed").get(),
|
||||
getter_Copies(mLivemarkFailed));
|
||||
if (NS_FAILED(rv)) {
|
||||
mLivemarkFailed.Assign(NS_LITERAL_STRING("Live Bookmark feed failed to load."));
|
||||
}
|
||||
|
||||
// Create timer to check whether to update livemarks
|
||||
if (!mTimer) {
|
||||
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a timer");
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
mTimer->InitWithFuncCallback(nsLivemarkService::FireTimer, this, LIVEMARK_TIMEOUT,
|
||||
nsITimer::TYPE_REPEATING_SLACK);
|
||||
// Note: don't addref "this" as we'll cancel the timer in the nsLivemarkService destructor
|
||||
}
|
||||
|
||||
// Use the annotations service to store data about livemarks
|
||||
mAnnotationService = do_GetService("@mozilla.org/browser/annotation-service;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Initialize the constants used to parse RDF livemark feeds
|
||||
nsCOMPtr<nsIRDFService> pRDF;
|
||||
pRDF = do_GetService(kRDFServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
pRDF->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
|
||||
&kLMRDF_type);
|
||||
pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "channel"),
|
||||
&kLMRSS09_channel);
|
||||
pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "item"),
|
||||
&kLMRSS09_item);
|
||||
pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "title"),
|
||||
&kLMRSS09_title);
|
||||
pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "link"),
|
||||
&kLMRSS09_link);
|
||||
|
||||
pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "channel"),
|
||||
&kLMRSS10_channel);
|
||||
pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "items"),
|
||||
&kLMRSS10_items);
|
||||
pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "title"),
|
||||
&kLMRSS10_title);
|
||||
pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "link"),
|
||||
&kLMRSS10_link);
|
||||
|
||||
pRDF->GetResource(NS_LITERAL_CSTRING(DC_NAMESPACE_URI "date"),
|
||||
&kLMDC_date);
|
||||
|
||||
// Initialize the list of livemarks from the list of URIs
|
||||
// that have a feed uri annotation.
|
||||
nsIURI ** pLivemarks;
|
||||
PRUint32 numLivemarks;
|
||||
rv = mAnnotationService->GetPagesWithAnnotation(NS_LITERAL_CSTRING(LMANNO_FEEDURI),
|
||||
&numLivemarks,
|
||||
&pLivemarks);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRInt32 i = 0; i < numLivemarks; ++i) {
|
||||
|
||||
// Get the feed URI from the annotation.
|
||||
nsAutoString feedURIString;
|
||||
rv = mAnnotationService->GetAnnotationString(pLivemarks[i],
|
||||
NS_LITERAL_CSTRING(LMANNO_FEEDURI),
|
||||
feedURIString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIURI> feedURI;
|
||||
rv = NS_NewURI(getter_AddRefs(feedURI), NS_ConvertUTF16toUTF8(feedURIString));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Use QueryStringToQueries() to get the folderId from the place:uri.
|
||||
// (It ends up in folders[0])
|
||||
nsCAutoString folderQueryString;
|
||||
rv = pLivemarks[i]->GetSpec(folderQueryString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
PRUint32 resultCount;
|
||||
nsINavHistoryQuery** queries = nsnull;
|
||||
nsCOMPtr<nsINavHistoryQueryOptions> options;
|
||||
rv = History()->QueryStringToQueries(folderQueryString,
|
||||
&queries,
|
||||
&resultCount,
|
||||
getter_AddRefs(options));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
PRUint32 folderCount;
|
||||
PRInt64 *folders = nsnull;
|
||||
rv = queries[0]->GetFolders(&folderCount, &folders);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Create the livemark and add it to the list.
|
||||
LivemarkInfo li(folders[0], pLivemarks[i], feedURI);
|
||||
PRBool added = mLivemarks.AppendElement(li);
|
||||
NS_ASSERTION(added, "Could not add livemark to array!");
|
||||
}
|
||||
if (numLivemarks > 0)
|
||||
nsMemory::Free(pLivemarks);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLivemarkService::CreateLivemark(PRInt64 aFolder,
|
||||
const nsAString &aName,
|
||||
nsIURI *aSiteURI,
|
||||
nsIURI *aFeedURI,
|
||||
PRInt32 aIndex,
|
||||
PRInt64 *aNewLivemark)
|
||||
{
|
||||
// Create the livemark as a bookmark container
|
||||
nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
|
||||
nsresult rv = bookmarks->CreateContainer(aFolder, aName, aIndex,
|
||||
NS_LITERAL_STRING(NS_LIVEMARKSERVICE_CONTRACTID),
|
||||
aNewLivemark);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Get the livemark URI
|
||||
nsCOMPtr<nsIURI> livemarkURI;
|
||||
rv = bookmarks->GetFolderURI(*aNewLivemark, getter_AddRefs(livemarkURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Add an annotation to map the folder URI to the livemark feed URI
|
||||
nsCAutoString feedURISpec;
|
||||
rv = aFeedURI->GetSpec(feedURISpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mAnnotationService->SetAnnotationString(livemarkURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_FEEDURI),
|
||||
NS_ConvertUTF8toUTF16(feedURISpec),
|
||||
0,
|
||||
nsIAnnotationService::EXPIRE_NEVER);
|
||||
|
||||
// Add an annotation to map the folder URI to the livemark site URI
|
||||
nsCAutoString siteURISpec;
|
||||
rv = aSiteURI->GetSpec(siteURISpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mAnnotationService->SetAnnotationString(livemarkURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_SITEURI),
|
||||
NS_ConvertUTF8toUTF16(siteURISpec),
|
||||
0,
|
||||
nsIAnnotationService::EXPIRE_NEVER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Store the livemark info in our array.
|
||||
LivemarkInfo info(*aNewLivemark, livemarkURI, aFeedURI);
|
||||
PRBool added = mLivemarks.AppendElement(info);
|
||||
NS_ASSERTION(added, "Could not add livemark to list!");
|
||||
|
||||
UpdateLivemarkChildren(mLivemarks.Length() - 1);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLivemarkService::OnContainerOpening(PRInt64 aContainer, PRBool * _result)
|
||||
{
|
||||
// Nothing to do here since the containers are filled in
|
||||
// as the livemarks are loaded through FireTimer().
|
||||
*_result = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLivemarkService::OnContainerClosed(PRInt64 aContainer)
|
||||
{
|
||||
// Nothing to clean up
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLivemarkService::OnContainerRemoving(PRInt64 aContainer)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Find the livemark id in the list of livemarks.
|
||||
PRInt32 lmIndex = -1;
|
||||
for (PRInt32 i = 0; i < mLivemarks.Length(); i++) {
|
||||
if (mLivemarks[i].folderId == aContainer) {
|
||||
lmIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If there livemark isn't in the list, it's not valid.
|
||||
if (lmIndex == -1)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
// Remove the annotations that link the folder URI to the
|
||||
// Feed URI and Site URI
|
||||
rv = mAnnotationService->RemoveAnnotation(mLivemarks[lmIndex].folderURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_FEEDURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mAnnotationService->RemoveAnnotation(mLivemarks[lmIndex].folderURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_SITEURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Check if there are any other livemarks pointing to this feed.
|
||||
// If not, remove the annotations for the feed.
|
||||
PRBool stillInUse = PR_FALSE;
|
||||
PRBool urisEqual = PR_FALSE;
|
||||
for (i = 0; i < mLivemarks.Length(); i++) {
|
||||
if (i != lmIndex &&
|
||||
(NS_OK == mLivemarks[i].feedURI->Equals(mLivemarks[lmIndex].feedURI, &urisEqual)) &&
|
||||
urisEqual) {
|
||||
stillInUse = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!stillInUse) {
|
||||
// No other livemarks use this feed. Clear all the annotations for it.
|
||||
rv = mAnnotationService->RemoveAnnotation(mLivemarks[lmIndex].feedURI,
|
||||
NS_LITERAL_CSTRING("livemark_expiration"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Take the annotation out of the list of annotations.
|
||||
mLivemarks.RemoveElementAt(lmIndex);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLivemarkService::OnContainerMoved(PRInt64 aContainer,
|
||||
PRInt64 aNewFolder,
|
||||
PRInt32 aNewIndex)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Find the livemark in the list.
|
||||
PRInt32 index = -1;
|
||||
for (PRInt32 i = 0; i < mLivemarks.Length(); i++) {
|
||||
if (mLivemarks[i].folderId == aContainer) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == -1)
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
|
||||
// Get the new uri
|
||||
nsCOMPtr<nsIURI> newURI;
|
||||
rv = nsNavBookmarks::GetBookmarksService()->GetFolderURI(aContainer,
|
||||
getter_AddRefs(newURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIURI> oldURI = mLivemarks[index].folderURI;
|
||||
mLivemarks[index].folderURI = newURI;
|
||||
|
||||
// Update the annotation that maps the folder URI to the livemark feed URI
|
||||
nsAutoString feedURIString;
|
||||
rv = mAnnotationService->GetAnnotationString(oldURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_FEEDURI),
|
||||
feedURIString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mAnnotationService->RemoveAnnotation(oldURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_FEEDURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mAnnotationService->SetAnnotationString(newURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_FEEDURI),
|
||||
feedURIString,
|
||||
0,
|
||||
nsIAnnotationService::EXPIRE_NEVER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Update the annotation that maps the folder URI to the livemark site URI
|
||||
nsAutoString siteURIString;
|
||||
rv = mAnnotationService->GetAnnotationString(oldURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_SITEURI),
|
||||
siteURIString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mAnnotationService->RemoveAnnotation(oldURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_SITEURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mAnnotationService->SetAnnotationString(newURI,
|
||||
NS_LITERAL_CSTRING(LMANNO_SITEURI),
|
||||
siteURIString,
|
||||
0,
|
||||
nsIAnnotationService::EXPIRE_NEVER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsLivemarkService::FireTimer(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
nsLivemarkService *lmks = NS_STATIC_CAST(nsLivemarkService *, aClosure);
|
||||
if (!lmks) return;
|
||||
|
||||
// Go through all of the livemarks, and check if each needs to be updated.
|
||||
for (PRInt32 i = 0; i < lmks->mLivemarks.Length(); i++) {
|
||||
lmks->UpdateLivemarkChildren(i);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsLivemarkService::DeleteLivemarkChildren(PRInt64 aLivemarkFolderId)
|
||||
{
|
||||
nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
|
||||
|
||||
// Get the folder children.
|
||||
nsresult rv = bookmarks->RemoveFolderChildren(aLivemarkFolderId);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsLivemarkService::InsertLivemarkChild(PRInt64 aLivemarkFolderId,
|
||||
nsIURI *aURI,
|
||||
const nsAString &aTitle)
|
||||
{
|
||||
nsresult rv;
|
||||
nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
|
||||
rv = bookmarks->InsertItem(aLivemarkFolderId, aURI, -1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = bookmarks->SetItemTitle(aURI, aTitle);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsLivemarkService::InsertLivemarkLoadingItem(PRInt64 aFolder)
|
||||
{
|
||||
nsresult rv;
|
||||
nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
|
||||
nsCOMPtr<nsIURI> loadingURI;
|
||||
rv = NS_NewURI(getter_AddRefs(loadingURI), NS_LITERAL_CSTRING("about:livemark-loading"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = bookmarks->InsertItem(aFolder, loadingURI, -1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = bookmarks->SetItemTitle(loadingURI, mLivemarkLoading);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsLivemarkService::InsertLivemarkFailedItem(PRInt64 aFolder)
|
||||
{
|
||||
nsresult rv;
|
||||
nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
|
||||
nsCOMPtr<nsIURI> failedURI;
|
||||
rv = NS_NewURI(getter_AddRefs(failedURI), NS_LITERAL_CSTRING("about:livemark-failed"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = bookmarks->InsertItem(aFolder, failedURI, -1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = bookmarks->SetItemTitle(failedURI, mLivemarkFailed);
|
||||
return rv;
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
/* -*- Mode: C++; tab-width: 2; 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 Places.
|
||||
*
|
||||
* 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):
|
||||
* Annie Sullivan <annie.sullivan@gmail.com> (original author)
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#include "nsILivemarkService.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsIAnnotationService.h"
|
||||
#include "nsNavHistory.h"
|
||||
#include "nsBrowserCompsCID.h"
|
||||
|
||||
// Constants for livemark annotations
|
||||
#define LMANNO_FEEDURI "livemark/feedURI"
|
||||
#define LMANNO_SITEURI "livemark/siteURI"
|
||||
#define LMANNO_EXPIRATION "livemark/expiration"
|
||||
|
||||
class nsLivemarkService : public nsILivemarkService
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIBOOKMARKSCONTAINER
|
||||
NS_DECL_NSILIVEMARKSERVICE
|
||||
|
||||
nsLivemarkService();
|
||||
nsresult Init();
|
||||
|
||||
// These functions are called by the livemarks feed loader
|
||||
// to set the livemark children.
|
||||
nsresult DeleteLivemarkChildren(PRInt64 aLivemarkFolderId);
|
||||
nsresult InsertLivemarkChild(PRInt64 aLivemarkFolderId,
|
||||
nsIURI *aURI,
|
||||
const nsAString &aTitle);
|
||||
nsresult InsertLivemarkLoadingItem(PRInt64 aFolder);
|
||||
nsresult InsertLivemarkFailedItem(PRInt64 aFolder);
|
||||
|
||||
struct LivemarkInfo {
|
||||
PRInt64 folderId;
|
||||
nsCOMPtr<nsIURI> folderURI;
|
||||
nsCOMPtr<nsIURI> feedURI;
|
||||
PRBool locked;
|
||||
|
||||
LivemarkInfo(PRInt64 aFolderId, nsCOMPtr<nsIURI> aFolderURI, nsCOMPtr<nsIURI> aFeedURI) {
|
||||
folderId = aFolderId;
|
||||
folderURI = aFolderURI;
|
||||
feedURI = aFeedURI;
|
||||
locked = false;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
static nsLivemarkService *sInstance;
|
||||
|
||||
~nsLivemarkService();
|
||||
|
||||
// remove me when there is better query initialization
|
||||
nsNavHistory* History() { return nsNavHistory::GetHistoryService(); }
|
||||
|
||||
// For localized "livemark loading...", "livemark failed to load",
|
||||
// etc. messages
|
||||
nsCOMPtr<nsIStringBundle> mBundle;
|
||||
nsXPIDLString mLivemarkLoading;
|
||||
nsXPIDLString mLivemarkFailed;
|
||||
|
||||
nsCOMPtr<nsIAnnotationService> mAnnotationService;
|
||||
nsCOMPtr<nsINavBookmarksService> mBookmarksService;
|
||||
|
||||
// The list of livemarks is stored in this array
|
||||
nsTArray<LivemarkInfo> mLivemarks;
|
||||
|
||||
// Livemarks are updated on a timer.
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
static void FireTimer(nsITimer* aTimer, void* aClosure);
|
||||
nsresult UpdateLivemarkChildren(PRInt32 aLivemarkIndex);
|
||||
};
|
|
@ -43,6 +43,7 @@
|
|||
#include "nsIServiceManager.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIAnnotationService.h"
|
||||
#include "nsIBookmarksContainer.h"
|
||||
|
||||
const PRInt32 nsNavBookmarks::kFindBookmarksIndex_ItemChild = 0;
|
||||
const PRInt32 nsNavBookmarks::kFindBookmarksIndex_FolderChild = 1;
|
||||
|
@ -51,6 +52,7 @@ const PRInt32 nsNavBookmarks::kFindBookmarksIndex_Position = 3;
|
|||
|
||||
const PRInt32 nsNavBookmarks::kGetFolderInfoIndex_FolderID = 0;
|
||||
const PRInt32 nsNavBookmarks::kGetFolderInfoIndex_Title = 1;
|
||||
const PRInt32 nsNavBookmarks::kGetFolderInfoIndex_Type = 2;
|
||||
|
||||
// These columns sit to the right of the kGetInfoIndex_* columns.
|
||||
const PRInt32 nsNavBookmarks::kGetChildrenIndex_Position = 8;
|
||||
|
@ -112,7 +114,8 @@ nsNavBookmarks::Init()
|
|||
if (!exists) {
|
||||
rv = dbConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("CREATE TABLE moz_bookmarks_folders ("
|
||||
"id INTEGER PRIMARY KEY, "
|
||||
"name LONGVARCHAR)"));
|
||||
"name LONGVARCHAR, "
|
||||
"type LONGVARCHAR)"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
@ -125,7 +128,7 @@ nsNavBookmarks::Init()
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT id, name FROM moz_bookmarks_folders WHERE id = ?1"),
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT id, name, type FROM moz_bookmarks_folders WHERE id = ?1"),
|
||||
getter_AddRefs(mDBGetFolderInfo));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -662,7 +665,7 @@ nsNavBookmarks::CreateFolder(PRInt64 aParent, const nsAString &aName,
|
|||
|
||||
{
|
||||
nsCOMPtr<mozIStorageStatement> statement;
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_folders (name) VALUES (?1)"),
|
||||
rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_folders (name, type) VALUES (?1, null)"),
|
||||
getter_AddRefs(statement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -698,10 +701,55 @@ nsNavBookmarks::CreateFolder(PRInt64 aParent, const nsAString &aName,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavBookmarks::CreateContainer(PRInt64 aParent, const nsAString &aName,
|
||||
PRInt32 aIndex, const nsAString &aType,
|
||||
PRInt64 *aNewFolder)
|
||||
{
|
||||
// Containers are wrappers around read-only folders, with a specific type.
|
||||
nsresult rv = CreateFolder(aParent, aName, aIndex, aNewFolder);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Make sure the container is read-only
|
||||
rv = SetFolderReadonly(*aNewFolder, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set the type.
|
||||
nsCOMPtr<mozIStorageStatement> statement;
|
||||
rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks_folders SET type = ?2 WHERE id = ?1"),
|
||||
getter_AddRefs(statement));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = statement->BindInt64Parameter(0, *aNewFolder);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = statement->BindStringParameter(1, aType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = statement->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavBookmarks::RemoveFolder(PRInt64 aFolder)
|
||||
{
|
||||
// If this is a container bookmark, try to notify its service.
|
||||
nsresult rv;
|
||||
nsAutoString folderType;
|
||||
rv = GetFolderType(aFolder, folderType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (folderType.Length() > 0) {
|
||||
// There is a type associated with this folder; it's a livemark.
|
||||
nsCOMPtr<nsIBookmarksContainer> bmcServ = do_GetService(NS_ConvertUTF16toUTF8(folderType).get());
|
||||
if (bmcServ) {
|
||||
rv = bmcServ->OnContainerRemoving(aFolder);
|
||||
if (NS_FAILED(rv))
|
||||
NS_WARNING("Remove folder container notification failed.");
|
||||
}
|
||||
}
|
||||
|
||||
mozIStorageConnection *dbConn = DBConn();
|
||||
mozStorageTransaction transaction(dbConn, PR_FALSE);
|
||||
|
||||
|
@ -709,7 +757,6 @@ nsNavBookmarks::RemoveFolder(PRInt64 aFolder)
|
|||
buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks WHERE folder_child = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
|
||||
nsresult rv;
|
||||
PRInt64 parent;
|
||||
PRInt32 index;
|
||||
{
|
||||
|
@ -728,6 +775,41 @@ nsNavBookmarks::RemoveFolder(PRInt64 aFolder)
|
|||
index = statement->AsInt32(1);
|
||||
}
|
||||
|
||||
// Remove all of the folder's children
|
||||
RemoveFolderChildren(aFolder);
|
||||
|
||||
// Remove the folder from its parent
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE folder_child = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
rv = dbConn->ExecuteSimpleSQL(buffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// And remove the folder from the folder table
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks_folders WHERE id = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
rv = dbConn->ExecuteSimpleSQL(buffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = AdjustIndices(parent, index + 1, PR_INT32_MAX, -1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = transaction.Commit();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
|
||||
OnFolderRemoved(aFolder, parent, index))
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolder)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCAutoString buffer;
|
||||
mozIStorageConnection *dbConn = DBConn();
|
||||
|
||||
// Now locate all of the folder children, so we can recursively remove them.
|
||||
nsTArray<PRInt64> folderChildren;
|
||||
{
|
||||
|
@ -754,30 +836,6 @@ nsNavBookmarks::RemoveFolder(PRInt64 aFolder)
|
|||
buffer.AppendInt(aFolder);
|
||||
rv = dbConn->ExecuteSimpleSQL(buffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE folder_child = ");
|
||||
// Remove the folder from its parent
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE folder_child = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
rv = dbConn->ExecuteSimpleSQL(buffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// And remove the folder from the folder table
|
||||
buffer.AssignLiteral("DELETE FROM moz_bookmarks_folders WHERE id = ");
|
||||
buffer.AppendInt(aFolder);
|
||||
rv = dbConn->ExecuteSimpleSQL(buffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = AdjustIndices(parent, index + 1, PR_INT32_MAX, -1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = transaction.Commit();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
|
||||
OnFolderRemoved(aFolder, parent, index))
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -968,6 +1026,59 @@ nsNavBookmarks::GetFolderTitle(PRInt64 aFolder, nsAString &aTitle)
|
|||
return mDBGetFolderInfo->GetString(kGetFolderInfoIndex_Title, aTitle);
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsNavBookmarks::GetFolderType(PRInt64 aFolder, nsAString &aType)
|
||||
{
|
||||
mozStorageStatementScoper scope(mDBGetFolderInfo);
|
||||
nsresult rv = mDBGetFolderInfo->BindInt64Parameter(0, aFolder);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool results;
|
||||
rv = mDBGetFolderInfo->ExecuteStep(&results);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!results) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return mDBGetFolderInfo->GetString(kGetFolderInfoIndex_Type, aType);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavBookmarks::GetFolderURI(PRInt64 aFolder, nsIURI **aURI)
|
||||
{
|
||||
// Create a query for the folder; the URI is the querystring
|
||||
// from that query.
|
||||
nsresult rv;
|
||||
nsNavHistory *history = History();
|
||||
nsCOMPtr<nsINavHistoryQuery> query;
|
||||
rv = history->GetNewQuery(getter_AddRefs(query));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = query->SetFolders(&aFolder, 1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsINavHistoryQueryOptions> queryOptions;
|
||||
rv = history->GetNewQueryOptions(getter_AddRefs(queryOptions));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
PRInt32 groupByFolder = nsINavHistoryQueryOptions::GROUP_BY_FOLDER;
|
||||
rv = queryOptions->SetGroupingMode(&groupByFolder, 1);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString folderURI;
|
||||
rv = history->QueriesToQueryString((nsINavHistoryQuery **)&query,
|
||||
1,
|
||||
queryOptions,
|
||||
folderURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Create a uri from the folder string.
|
||||
rv = NS_NewURI(aURI, folderURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavBookmarks::GetFolderReadonly(PRInt64 aFolder, PRBool *aResult)
|
||||
{
|
||||
|
@ -1032,15 +1143,8 @@ nsNavBookmarks::ResultNodeForFolder(PRInt64 aID,
|
|||
nsINavHistoryQueryOptions *aOptions,
|
||||
nsNavHistoryResultNode **aNode)
|
||||
{
|
||||
mozStorageStatementScoper scope(mDBGetFolderInfo);
|
||||
mDBGetFolderInfo->BindInt64Parameter(0, aID);
|
||||
|
||||
PRBool results;
|
||||
nsresult rv = mDBGetFolderInfo->ExecuteStep(&results);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(results, "ResultNodeForFolder expects a valid folder id");
|
||||
|
||||
// Create Query and QueryOptions objects to generate this folder's children
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsINavHistoryQuery> query;
|
||||
rv = aQuery->Clone(getter_AddRefs(query));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -1063,7 +1167,7 @@ nsNavBookmarks::ResultNodeForFolder(PRInt64 aID,
|
|||
node->mQueryCount = 1;
|
||||
node->mOptions = do_QueryInterface(options);
|
||||
|
||||
rv = FillFolderNode(mDBGetFolderInfo, node);
|
||||
rv = FillFolderNode(aID, node);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ADDREF(*aNode = node);
|
||||
|
@ -1071,10 +1175,24 @@ nsNavBookmarks::ResultNodeForFolder(PRInt64 aID,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsNavBookmarks::FillFolderNode(mozIStorageValueArray *aRow,
|
||||
nsNavHistoryResultNode *aNode)
|
||||
nsNavBookmarks::FillFolderNode(PRInt64 aID,
|
||||
nsNavHistoryQueryNode *aNode)
|
||||
{
|
||||
return aRow->GetString(kGetFolderInfoIndex_Title, aNode->mTitle);
|
||||
mozStorageStatementScoper scope(mDBGetFolderInfo);
|
||||
mDBGetFolderInfo->BindInt64Parameter(0, aID);
|
||||
|
||||
PRBool results;
|
||||
nsresult rv = mDBGetFolderInfo->ExecuteStep(&results);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(results, "ResultNodeForFolder expects a valid folder id");
|
||||
|
||||
// Fill in the folder type
|
||||
// aNode->mFolderType should get set to the empty string
|
||||
// if the folder type is null.
|
||||
rv = mDBGetFolderInfo->GetString(kGetFolderInfoIndex_Type, aNode->mFolderType);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
// Fill in the folder title
|
||||
return mDBGetFolderInfo->GetString(kGetFolderInfoIndex_Title, aNode->mTitle);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -70,10 +70,10 @@ public:
|
|||
nsINavHistoryQueryOptions *aOptions,
|
||||
nsNavHistoryResultNode **aNode);
|
||||
|
||||
// Fills in a ResultNode for a folder, using the given result row.
|
||||
// Fills in a ResultNode for the given folder.
|
||||
// The node's type and queries must already be set.
|
||||
nsresult FillFolderNode(mozIStorageValueArray *aRow,
|
||||
nsNavHistoryResultNode *aNode);
|
||||
nsresult FillFolderNode(PRInt64 aID,
|
||||
nsNavHistoryQueryNode *aNode);
|
||||
|
||||
// Find all the children of a folder, using the given query and options.
|
||||
// For each child, a ResultNode is created and added to |children|.
|
||||
|
@ -87,6 +87,7 @@ public:
|
|||
// constants for the above statement
|
||||
static const PRInt32 kGetFolderInfoIndex_FolderID;
|
||||
static const PRInt32 kGetFolderInfoIndex_Title;
|
||||
static const PRInt32 kGetFolderInfoIndex_Type;
|
||||
|
||||
private:
|
||||
static nsNavBookmarks *sInstance;
|
||||
|
@ -102,6 +103,7 @@ private:
|
|||
PRInt32 aStartIndex, PRInt32 aEndIndex,
|
||||
PRInt32 aDelta);
|
||||
PRInt32 FolderCount(PRInt64 aFolder);
|
||||
nsresult GetFolderType(PRInt64 aFolder, nsAString &aType);
|
||||
|
||||
// remove me when there is better query initialization
|
||||
nsNavHistory* History() { return nsNavHistory::GetHistoryService(); }
|
||||
|
|
|
@ -261,6 +261,7 @@ public:
|
|||
// nsINavHistoryResultNode methods
|
||||
NS_IMETHOD GetFolderId(PRInt64 *aId)
|
||||
{ *aId = nsNavHistoryQueryNode::GetFolderId(); return NS_OK; }
|
||||
NS_IMETHOD GetFolderType(nsAString& aFolderType);
|
||||
NS_IMETHOD GetQueries(PRUint32 *aQueryCount,
|
||||
nsINavHistoryQuery ***aQueries);
|
||||
NS_IMETHOD GetQueryOptions(nsINavHistoryQueryOptions **aOptions);
|
||||
|
@ -284,6 +285,7 @@ protected:
|
|||
PRUint32 mQueryCount;
|
||||
nsCOMPtr<nsNavHistoryQueryOptions> mOptions;
|
||||
PRBool mBuiltChildren;
|
||||
nsString mFolderType;
|
||||
|
||||
friend class nsNavBookmarks;
|
||||
};
|
||||
|
|
|
@ -141,6 +141,13 @@ NS_IMETHODIMP nsNavHistoryResultNode::GetTitle(nsAString& aTitle)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* attribute string folderType; */
|
||||
NS_IMETHODIMP nsNavHistoryResultNode::GetFolderType(nsAString& aFolderType)
|
||||
{
|
||||
aFolderType.Truncate(0);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* attribute PRInt32 accessCount; */
|
||||
NS_IMETHODIMP nsNavHistoryResultNode::GetAccessCount(PRInt32 *aAccessCount)
|
||||
{
|
||||
|
@ -414,6 +421,14 @@ nsNavHistoryQueryNode::GetFolderId() const
|
|||
return id;
|
||||
}
|
||||
|
||||
/* attribute string folderType; */
|
||||
NS_IMETHODIMP
|
||||
nsNavHistoryQueryNode::GetFolderType(nsAString& aFolderType)
|
||||
{
|
||||
aFolderType = mFolderType;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavHistoryQueryNode::GetQueries(PRUint32 *aQueryCount,
|
||||
nsINavHistoryQuery ***aQueries)
|
||||
|
@ -951,16 +966,7 @@ nsNavHistoryQueryNode::Rebuild()
|
|||
PRInt64 folderId = GetFolderId();
|
||||
if (folderId != 0) {
|
||||
nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
|
||||
mozIStorageStatement *statement = bookmarks->DBGetFolderInfo();
|
||||
mozStorageStatementScoper scope(statement);
|
||||
|
||||
nsresult rv = statement->BindInt64Parameter(0, folderId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
PRBool results;
|
||||
rv = statement->ExecuteStep(&results);
|
||||
NS_ASSERTION(results, "folder must be in db");
|
||||
|
||||
return bookmarks->FillFolderNode(statement, this);
|
||||
return bookmarks->FillFolderNode(folderId, this);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1008,6 +1014,12 @@ nsNavHistoryResult::nsNavHistoryResult(nsNavHistory* aHistoryService,
|
|||
if (aOptions)
|
||||
aOptions->Clone(getter_AddRefs(mOptions));
|
||||
|
||||
PRInt64 folderId = 0;
|
||||
GetFolderId(&folderId);
|
||||
if (folderId != 0) {
|
||||
nsNavBookmarks::GetBookmarksService()->FillFolderNode(folderId, this);
|
||||
}
|
||||
|
||||
mType = nsINavHistoryResult::RESULT_TYPE_QUERY;
|
||||
mVisibleIndex = -1;
|
||||
mExpanded = PR_TRUE; // the result itself can never be "collapsed"
|
||||
|
|
Загрузка…
Ссылка в новой задаче