Added nsIBookmarksContainer interface which allows custom bookmarks types.

Implemented nsILivemarksService as a nsIBookmarksContainer.
bug=317837 r=beng sr=bryner
This commit is contained in:
annie.sullivan%gmail.com 2005-12-15 20:56:18 +00:00
Родитель ea5ff41b60
Коммит b2ff11200f
22 изменённых файлов: 1887 добавлений и 61 удалений

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

@ -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"