Bug 353434. Remove C++ feed parsing for livemarks on trunk (places). r=dietrich

This commit is contained in:
sayrer%gmail.com 2006-10-19 02:24:30 +00:00
parent abed647846
commit 7e42eae088
15 changed files with 718 additions and 1933 deletions

View File

@ -96,7 +96,7 @@ var BookmarkPropertiesPanel = {
get _livemarks() {
if (!this.__livemarks) {
this.__livemarks =
Cc["@mozilla.org/browser/livemark-service;1"].
Cc["@mozilla.org/browser/livemark-service;2"].
getService(Ci.nsILivemarkService);
}
return this.__livemarks;

View File

@ -241,7 +241,7 @@ var PlacesController = {
get livemarks() {
if (!this._livemarks) {
this._livemarks =
Cc["@mozilla.org/browser/livemark-service;1"].
Cc["@mozilla.org/browser/livemark-service;2"].
getService(Ci.nsILivemarkService);
}
return this._livemarks;
@ -575,7 +575,7 @@ var PlacesController = {
*/
nodeIsLivemarkContainer: function PC_nodeIsLivemarkContainer(node) {
return (this.nodeIsRemoteContainer(node) &&
asContainer(node).remoteContainerType == "@mozilla.org/browser/livemark-service;1");
asContainer(node).remoteContainerType == "@mozilla.org/browser/livemark-service;2");
},
/**
@ -2248,7 +2248,7 @@ function PlacesBaseTransaction() {
PlacesBaseTransaction.prototype = {
bookmarks: Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService),
livemarks: Cc["@mozilla.org/browser/livemark-service;1"].
livemarks: Cc["@mozilla.org/browser/livemark-service;2"].
getService(Ci.nsILivemarkService),
LOG: LOG,
redoTransaction: function PIT_redoTransaction() {

View File

@ -234,7 +234,7 @@
Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
var lms =
Cc["@mozilla.org/browser/livemark-service;1"].
Cc["@mozilla.org/browser/livemark-service;2"].
getService(Ci.nsILivemarkService);
var siteURI = lms.getSiteURI(folder);
if (siteURI) {

View File

@ -117,7 +117,7 @@
Cc["@mozilla.org/browser/annotation-service;1"].
getService(Ci.nsIAnnotationService);
this._lms =
Cc["@mozilla.org/browser/livemark-service;1"].
Cc["@mozilla.org/browser/livemark-service;2"].
getService(Ci.nsILivemarkService);
this._bms.addObserver(this._observer, false);

View File

@ -76,3 +76,9 @@ function uri(spec) {
return iosvc.newURI(spec, null, null);
}
// Get bookmark service
try {
var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
} catch(ex) {
do_throw("Could not get nav-bookmarks-service\n");
}

View File

@ -37,13 +37,6 @@
*
* ***** END LICENSE BLOCK ***** */
// Get bookmark service
try {
var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
} catch(ex) {
do_throw("Could not get nav-bookmarks-service\n");
}
// create and add bookmarks observer
var observer = {
onBeginUpdateBatch: function() {

View File

@ -39,17 +39,10 @@
// Get livemark service
try {
var lmsvc = Cc["@mozilla.org/browser/livemark-service;1"].getService(Ci.nsILivemarkService);
var lmsvc = Cc["@mozilla.org/browser/livemark-service;2"].getService(Ci.nsILivemarkService);
} catch(ex) {
do_throw("Could not get livemark-service\n");
}
// Get bookmark service
try {
var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
} catch(ex) {
do_throw("Could not get nav-bookmarks-service\n");
}
}
// get bookmarks root index
var root = bmsvc.bookmarksRoot;
@ -58,17 +51,17 @@ var root = bmsvc.bookmarksRoot;
function run_test() {
var livemarkId = lmsvc.createLivemark(root, "foo", uri("http://example.com/"),
uri("http://example.com/rss.xml"), -1);
do_check_true(lmsvc.isLivemark(livemarkId));
do_check_true(lmsvc.getSiteURI(livemarkId).spec == "http://example.com/");
do_check_true(lmsvc.getFeedURI(livemarkId).spec == "http://example.com/rss.xml");
lmsvc.setSiteURI(livemarkId, uri("http://foo.example.com/"));
do_check_true(lmsvc.getSiteURI(livemarkId).spec == "http://foo.example.com/");
lmsvc.setFeedURI(livemarkId, uri("http://foo.example.com/rss.xml"));
do_check_true(lmsvc.getFeedURI(livemarkId).spec == "http://foo.example.com/rss.xml");
//XXXsayrer this fails with the current code, "livemark-service;1"
// Make sure we can't add a livemark to a livemark
var livemarkId2 = null;
try {
@ -77,7 +70,7 @@ function run_test() {
} catch (ex) {
livemarkId2 = null;
}
///do_check_true(livemarkId2 == null);
do_check_true(livemarkId2 == null);
// make sure it didn't screw up the first one
do_check_true(lmsvc.isLivemark(livemarkId));

View File

@ -95,7 +95,7 @@
"@mozilla.org/browser/nav-bookmarks-service;1"
#define NS_LIVEMARKSERVICE_CONTRACTID \
"@mozilla.org/browser/livemark-service;1"
"@mozilla.org/browser/livemark-service;2"
#define NS_MORKHISTORYIMPORTER_CONTRACTID \
"@mozilla.org/browser/history-importer;1"
@ -168,9 +168,6 @@
#define NS_NAVBOOKMARKSSERVICE_CID \
{ 0x9de95a0c, 0x39a4, 0x4d64, {0x9a, 0x53, 0x17, 0x94, 0x0d, 0xd7, 0xca, 0xbb}}
#define NS_LIVEMARKSERVICE_CID \
{ 0xb1257934, 0x86cf, 0x4143, { 0x83, 0x86, 0x73, 0x4a, 0xc3, 0x52, 0xb6, 0xba } }
#define NS_MORKHISTORYIMPORTER_CID \
{ 0x428e6d12, 0x9c6d, 0x436f, {0xb7, 0xa3, 0x6c, 0xa5, 0xf4, 0x80, 0x92, 0x12}}

View File

@ -89,15 +89,11 @@ CPPSRCS = \
nsNavHistoryQuery.cpp \
nsNavHistoryResult.cpp \
nsNavBookmarks.cpp \
nsLivemarkService.cpp \
nsBookmarksFeedHandler.cpp \
nsMaybeWeakPtr.cpp \
nsMorkHistoryImporter.cpp \
nsPlacesModule.cpp \
$(NULL)
include $(topsrcdir)/config/rules.mk
EXTRA_DSO_LDOPTS += \
$(DEPTH)/db/morkreader/$(LIB_PREFIX)morkreader_s.$(LIB_SUFFIX) \
$(MOZ_UNICHARUTIL_LIBS) \
@ -105,3 +101,7 @@ EXTRA_DSO_LDOPTS += \
$(NULL)
LOCAL_INCLUDES += -I$(srcdir)/../../build
EXTRA_PP_COMPONENTS = nsLivemarkService.js
include $(topsrcdir)/config/rules.mk

View File

@ -1,941 +0,0 @@
/* -*- 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)
* Robert Sayre <sayrer@gmail.com>
* Masayuki Nakano <masayuki@d-toybox.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 ***** */
// THE ORIGINAL LOCATION OF THIS CODE IS
// /browser/components/bookmarks/src/nsBookmarksFeedHandler.cpp
#include "nsLivemarkService.h"
#include "nsArrayEnumerator.h"
#include "nsIArray.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 "nsStringStream.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"
#include "nsIDOM3Node.h"
#include "nsIDOMDocumentTraversal.h"
#include "nsIDOMTreeWalker.h"
#include "nsIDOMNodeFilter.h"
#include "nsIDOMDocumentFragment.h"
#include "nsIParser.h"
#include "nsParserCIID.h"
#include "nsIFragmentContentSink.h"
#include "nsIContentSink.h"
#include "nsIDocument.h"
#include "nsNavBookmarks.h"
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
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 FindTextInChildNodes (nsIDOMNode *aNode, nsAString &aString);
NS_METHOD ParseHTMLFragment(nsAString &aFragString, nsIDocument* aTargetDocument, nsIDOMNode **outNode);
PRBool IsLinkValid(const PRUnichar *aURI);
nsLivemarkService *mLivemarkService;
nsCOMPtr<nsIAnnotationService> mAnnotationService;
nsRefPtr<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;
}
// enclose all the changes (deletes and a bunch of adds) in a batch to
// avoid UI and DB updates
nsBookmarksUpdateBatcher bookmarksBatch;
// 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;
nsCOMPtr<nsIStreamListener> listener;
rv = rdfparser->ParseAsync(ds, mLivemark->feedURI, getter_AddRefs(listener));
if (NS_FAILED(rv)) return rv;
if (!listener) return NS_ERROR_FAILURE;
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewCStringInputStream(getter_AddRefs(stream), mBody);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIChannel> channel;
rv = NS_NewInputStreamChannel(getter_AddRefs(channel), mLivemark->feedURI,
stream, NS_LITERAL_CSTRING("text/xml"));
if (NS_FAILED(rv)) return rv;
listener->OnStartRequest(channel, nsnull);
listener->OnDataAvailable(channel, nsnull, stream, 0, mBody.Length());
listener->OnStopRequest(channel, nsnull, NS_OK);
// 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(mLivemarkService->mLMRDF_type, mLivemarkService->mLMRSS10_channel, PR_TRUE, getter_AddRefs(channelResource));
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
if (rv == NS_OK) {
RSS_items = mLivemarkService->mLMRSS10_items;
RSS_title = mLivemarkService->mLMRSS10_title;
RSS_link = mLivemarkService->mLMRSS10_link;
} else {
// try RSS 0.9
rv = ds->GetSource(mLivemarkService->mLMRDF_type, mLivemarkService->mLMRSS09_channel, PR_TRUE, getter_AddRefs(channelResource));
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
if (rv == NS_OK) {
RSS_items = nsnull;
RSS_title = mLivemarkService->mLMRSS09_title;
RSS_link = mLivemarkService->mLMRSS09_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(mLivemarkService->mLMRDF_type, mLivemarkService->mLMRSS09_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;
}
nsresult
nsLivemarkLoadListener::ParseHTMLFragment(nsAString &aFragString,
nsIDocument* aTargetDocument,
nsIDOMNode **outNode)
{
NS_ENSURE_ARG(aTargetDocument);
nsresult rv;
nsCOMPtr<nsIParser> parser;
parser = do_CreateInstance(kCParserCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// create the html fragment sink
nsCOMPtr<nsIContentSink> sink;
sink = do_CreateInstance(NS_HTMLFRAGMENTSINK2_CONTRACTID);
NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE);
nsCOMPtr<nsIFragmentContentSink> fragSink(do_QueryInterface(sink));
NS_ENSURE_TRUE(fragSink, NS_ERROR_FAILURE);
fragSink->SetTargetDocument(aTargetDocument);
// parse the fragment
parser->SetContentSink(sink);
#ifdef MOZILLA_1_8_BRANCH
parser->Parse(aFragString, (void*)0, NS_LITERAL_CSTRING("text/html"), PR_FALSE, PR_TRUE, eDTDMode_fragment);
#else
parser->Parse(aFragString, (void*)0, NS_LITERAL_CSTRING("text/html"), PR_TRUE, eDTDMode_fragment);
#endif
// get the fragment node
nsCOMPtr<nsIDOMDocumentFragment> contextfrag;
rv = fragSink->GetFragment(getter_AddRefs(contextfrag));
NS_ENSURE_SUCCESS(rv, rv);
rv = CallQueryInterface (contextfrag, outNode);
return rv;
}
// find all of the text and CDATA nodes below aNode and return them as a string
nsresult
nsLivemarkLoadListener::FindTextInChildNodes (nsIDOMNode *aNode, nsAString &aString)
{
NS_ENSURE_ARG(aNode);
nsresult rv;
nsCOMPtr<nsIDOMDocument> aDoc;
aNode->GetOwnerDocument(getter_AddRefs(aDoc));
nsCOMPtr<nsIDOMDocumentTraversal> trav = do_QueryInterface(aDoc, &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMTreeWalker> walker;
rv = trav->CreateTreeWalker(aNode,
nsIDOMNodeFilter::SHOW_TEXT | nsIDOMNodeFilter::SHOW_CDATA_SECTION,
nsnull, PR_TRUE, getter_AddRefs(walker));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMNode> currentNode;
walker->GetCurrentNode(getter_AddRefs(currentNode));
nsCOMPtr<nsIDOMCharacterData> charTextNode;
nsAutoString tempString;
while (currentNode) {
charTextNode = do_QueryInterface(currentNode);
if (charTextNode) {
charTextNode->GetData(tempString);
aString.Append(tempString);
}
walker->NextNode(getter_AddRefs(currentNode));
}
if (aString.IsEmpty()) {
return NS_ERROR_FAILURE;
} else {
return NS_OK;
}
}
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, mLivemarkService->mLMDC_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),
nsDependentString(linkStr));
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;
// XXXbz is this the right principal?
parser->Init(nsnull, mLivemark->feedURI, nsnull);
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
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"))) {
if (isAtom) {
/* Atom titles can contain HTML, so we need to find and remove it */
nsCOMPtr<nsIDOMElement> titleElem = do_QueryInterface(childNode);
if (!titleElem) break; // out of while(childNode) loop
nsAutoString titleMode; // Atom 0.3 only
nsAutoString titleType;
titleElem->GetAttribute(NS_LITERAL_STRING("type"), titleType);
titleElem->GetAttribute(NS_LITERAL_STRING("mode"), titleMode);
/* No one does this in <title> except standards pedants making test feeds,
* Atom 0.3 is deprecated, and RFC 4287 doesn't allow it
*/
if (titleMode.EqualsLiteral("base64")) {
break; // out of while(childNode) loop
}
/* Cover Atom 0.3 and RFC 4287 together */
if (titleType.EqualsLiteral("text") ||
titleType.EqualsLiteral("text/plain") ||
titleType.IsEmpty())
{
rv = FindTextInChildNodes(childNode, titleStr);
} else if (titleType.EqualsLiteral("html") || // RFC 4287
(titleType.EqualsLiteral("text/html") && // Atom 0.3
!titleMode.EqualsLiteral("xml")) ||
titleMode.EqualsLiteral("escaped")) // Atom 0.3
{
nsAutoString escapedHTMLStr;
rv = FindTextInChildNodes(childNode, escapedHTMLStr);
if (NS_FAILED(rv)) break; // out of while(childNode) loop
nsCOMPtr<nsIDOMNode> newNode;
nsCOMPtr<nsIDocument> doc = do_QueryInterface(xmldoc);
ParseHTMLFragment(escapedHTMLStr, doc, getter_AddRefs(newNode));
rv = FindTextInChildNodes(newNode, titleStr);
}else if (titleType.EqualsLiteral("xhtml") || // RFC 4287
titleType.EqualsLiteral("application/xhtml") || // Atom 0.3
titleMode.EqualsLiteral("xml")) // Atom 0.3
{
rv = FindTextInChildNodes(childNode, titleStr);
} else {
break; // out of while(childNode) loop
}
} else {
rv = FindTextInChildNodes(childNode, titleStr);
}
if (NS_FAILED(rv)) break;
} else if (childNname.Equals(NS_LITERAL_STRING("pubDate")) ||
childNname.Equals(NS_LITERAL_STRING("updated")))
{
rv = FindTextInChildNodes (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 = FindTextInChildNodes (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
nsCOMPtr<nsIDOM3Node> linkElem3 = do_QueryInterface(childNode);
if (linkElem3) {
// get the BaseURI (as string)
nsAutoString base;
rv = linkElem3->GetBaseURI(base);
if (NS_SUCCEEDED(rv) && !base.IsEmpty()) {
// convert a baseURI (string) to a nsIURI
nsCOMPtr<nsIURI> baseURI;
rv = NS_NewURI(getter_AddRefs(baseURI), base);
if (baseURI) {
nsString absLinkStr;
if (NS_SUCCEEDED(NS_MakeAbsoluteURI(absLinkStr, linkStr, baseURI)))
linkStr = absLinkStr;
}
}
}
}
} else if (linkStr.IsEmpty()) {
// in node's TEXT
rv = FindTextInChildNodes (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;
}
// Clean up whitespace
titleStr.CompressWhitespace();
linkStr.Trim("\b\t\r\n ");
dateStr.CompressWhitespace();
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),
nsDependentString(linkStr));
if (NS_FAILED(rv)) return rv;
}
}
}
nsCOMPtr<nsIDOMNode> temp;
rv = node->GetNextSibling(getter_AddRefs(temp));
if (NS_FAILED(rv)) return rv;
node = temp;
}
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. If aForceUpdate
* is true, the livemark will be updated whether or not it has expired; this
* is intended for user "Update Livemark" commands.
*/
nsresult
nsLivemarkService::UpdateLivemarkChildren(PRInt32 aLivemarkIndex, PRBool aForceUpdate)
{
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. We perform this
// check even when the update is being forced, in case the
// livemark has somehow never been loaded.
PRTime exprTime;
rv = mAnnotationService->GetAnnotationInt64(mLivemarks[aLivemarkIndex]->feedURI,
NS_LITERAL_CSTRING(LMANNO_EXPIRATION),
&exprTime);
if (rv == NS_OK) {
if (! aForceUpdate) {
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);
}
// Create a load group for the request. This will allow us to automatically
// keep track of redirects, so we can always cancel the channel.
nsCOMPtr<nsILoadGroup> loadGroup;
rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), nsnull);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> uriChannel;
rv = NS_NewChannel(getter_AddRefs(uriChannel),
mLivemarks[aLivemarkIndex]->feedURI,
nsnull, loadGroup, 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;
}
mLivemarks[aLivemarkIndex]->loadGroup = loadGroup;
return NS_OK;
}

View File

@ -96,7 +96,7 @@
#include "nsIAnnotationService.h"
#include "nsIFile.h"
#include "nsIHTMLContentSink.h"
#include "nsLivemarkService.h"
#include "nsILivemarkService.h"
#include "nsIParser.h"
#include "nsIServiceManager.h"
#include "nsNavBookmarks.h"
@ -1455,7 +1455,7 @@ nsNavBookmarks::WriteLivemark(PRInt64 aFolderId, const nsCString& aIndent,
PRUint32 dummy;
nsresult rv;
nsLivemarkService* livemarkService = nsLivemarkService::GetLivemarkService();
nsCOMPtr<nsILivemarkService> livemarkService(do_GetService(NS_LIVEMARKSERVICE_CONTRACTID, &rv));
NS_ENSURE_TRUE(livemarkService, NS_ERROR_UNEXPECTED);
// indent
@ -1605,7 +1605,7 @@ nsNavBookmarks::WriteContainerContents(PRInt64 aFolder, const nsCString& aIndent
}
// it could be a regular folder or it could be a livemark
nsLivemarkService* livemarkService = nsLivemarkService::GetLivemarkService();
nsCOMPtr<nsILivemarkService> livemarkService(do_GetService(NS_LIVEMARKSERVICE_CONTRACTID, &rv));
NS_ENSURE_TRUE(livemarkService, NS_ERROR_UNEXPECTED);
PRBool isLivemark;
rv = livemarkService->IsLivemark(folderId, &isLivemark);

View File

@ -1,778 +0,0 @@
/* -*- 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)
* Joe Hughes <joe@retrovirus.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 "nsIRDFService.h"
#include "nsLivemarkService.h"
#include "nsIServiceManager.h"
#include "nsNavBookmarks.h"
#include "nsNavHistoryResult.h"
#include "nsIAnnotationService.h"
#include "nsFaviconService.h"
#include "nsNetUtil.h"
#include "rdf.h"
#include "nsRDFCID.h"
#include "nsIObserverService.h"
#include "nsCRT.h"
#include "nsXPCOM.h"
#define LIVEMARK_TIMEOUT 15000 // fire every 15 seconds
#define PLACES_STRING_BUNDLE_URI "chrome://browser/locale/places/places.properties"
#define LIVEMARK_ICON_URI "chrome://browser/skin/places/livemarkItem.png"
// 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
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;
}
NS_IMPL_ISUPPORTS3(nsLivemarkService,
nsILivemarkService,
nsIRemoteContainer,
nsIObserver)
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 folder icon uri.
rv = NS_NewURI(getter_AddRefs(mIconURI), NS_LITERAL_STRING(LIVEMARK_ICON_URI));
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."));
}
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
// 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"),
getter_AddRefs(mLMRDF_type));
pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "channel"),
getter_AddRefs(mLMRSS09_channel));
pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "item"),
getter_AddRefs(mLMRSS09_item));
pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "title"),
getter_AddRefs(mLMRSS09_title));
pRDF->GetResource(NS_LITERAL_CSTRING(RSS09_NAMESPACE_URI "link"),
getter_AddRefs(mLMRSS09_link));
pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "channel"),
getter_AddRefs(mLMRSS10_channel));
pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "items"),
getter_AddRefs(mLMRSS10_items));
pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "title"),
getter_AddRefs(mLMRSS10_title));
pRDF->GetResource(NS_LITERAL_CSTRING(RSS10_NAMESPACE_URI "link"),
getter_AddRefs(mLMRSS10_link));
pRDF->GetResource(NS_LITERAL_CSTRING(DC_NAMESPACE_URI "date"),
getter_AddRefs(mLMDC_date));
// Initialize the list of livemarks from the list of URIs
// that have a feed uri annotation.
nsCOMArray<nsIURI> pLivemarks;
rv = mAnnotationService->GetPagesWithAnnotationCOMArray(
NS_LITERAL_CSTRING(LMANNO_FEEDURI), &pLivemarks);
NS_ENSURE_SUCCESS(rv, rv);
for (PRInt32 i = 0; i < pLivemarks.Count(); ++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 QueryStringToQueryArray() to get the folderId from the place:uri.
// (It ends up in folders[0] since we know that this place: uri was
// generated by the bookmark service to uniquely identify the folder)
nsCAutoString folderQueryString;
rv = pLivemarks[i]->GetSpec(folderQueryString);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMArray<nsNavHistoryQuery> queries;
nsCOMPtr<nsNavHistoryQueryOptions> options;
rv = History()->QueryStringToQueryArray(folderQueryString, &queries,
getter_AddRefs(options));
NS_ENSURE_SUCCESS(rv, rv);
if (queries.Count() != 1 || queries[0]->Folders().Length() != 1)
continue; // invalid folder URI (should identify exactly one folder)
// Create the livemark and add it to the list.
LivemarkInfo *li = new LivemarkInfo(queries[0]->Folders()[0],
pLivemarks[i], feedURI);
NS_ENSURE_TRUE(li, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(mLivemarks.AppendElement(li), NS_ERROR_OUT_OF_MEMORY);
}
return rv;
}
NS_IMETHODIMP
nsLivemarkService::Observe(nsISupports *aSubject, const char *aTopic,
const PRUnichar *aData)
{
if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
nsresult rv;
// Clear timer and pending loads to prevent leaks of the livemark service
// during shutdown.
if (mTimer) {
// be sure to cancel the timer, as it holds a
// weak reference back to nsLivemarkService
mTimer->Cancel();
mTimer = nsnull;
}
// Cancel any pending loads
for (PRUint32 i = 0; i < mLivemarks.Length(); ++i) {
LivemarkInfo *li = mLivemarks[i];
if (li->loadGroup) {
li->loadGroup->Cancel(NS_BINDING_ABORTED);
}
}
// Remove our xpcom-shutdown observer so we don't leak the observer
// service.
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
}
return NS_OK;
}
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,
NS_LITERAL_STRING(NS_LIVEMARKSERVICE_CONTRACTID),
aIndex, 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);
// Set the icon for the livemark
nsFaviconService* faviconService = nsFaviconService::GetFaviconService();
NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY);
faviconService->SetFaviconUrlForPage(livemarkURI, mIconURI);
if (aSiteURI) {
// 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 = new LivemarkInfo(*aNewLivemark, livemarkURI, aFeedURI);
NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(mLivemarks.AppendElement(info), NS_ERROR_OUT_OF_MEMORY);
UpdateLivemarkChildren(mLivemarks.Length() - 1, PR_FALSE);
return NS_OK;
}
NS_IMETHODIMP
nsLivemarkService::IsLivemark(PRInt64 aFolder, PRBool *aResult)
{
nsresult rv;
*aResult = PR_FALSE;
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_FAILURE);
nsCOMPtr<nsIURI> folderURI;
rv = bookmarks->GetFolderURI(aFolder, getter_AddRefs(folderURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = mAnnotationService->HasAnnotation(
folderURI, NS_LITERAL_CSTRING(LMANNO_FEEDURI), aResult);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsLivemarkService::GetSiteURI(PRInt64 aContainer, nsIURI **aURI)
{
nsresult rv;
// First off, make sure we're dealing with a livemark ID.
PRBool isLivemark = PR_FALSE;
rv = IsLivemark(aContainer, &isLivemark);
NS_ENSURE_SUCCESS(rv, rv);
if (!isLivemark)
return NS_ERROR_INVALID_ARG;
// Now convert the container ID to a container URI for annotation operations
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_FAILURE);
nsCOMPtr<nsIURI> containerURI;
rv = bookmarks->GetFolderURI(aContainer, getter_AddRefs(containerURI));
NS_ENSURE_SUCCESS(rv, rv);
// If there's no site URI annotation, return null
nsAutoString siteURIString;
rv = mAnnotationService->GetAnnotationString(
containerURI, NS_LITERAL_CSTRING(LMANNO_SITEURI), siteURIString);
if (NS_FAILED(rv)) {
*aURI = nsnull;
return NS_OK;
}
nsCOMPtr<nsIURI> siteURI;
rv = NS_NewURI(getter_AddRefs(siteURI), NS_ConvertUTF16toUTF8(siteURIString));
NS_ENSURE_SUCCESS(rv, rv);
*aURI = siteURI.get();
NS_IF_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
nsLivemarkService::SetSiteURI(PRInt64 aContainer, nsIURI *aSiteURI)
{
nsresult rv;
// First off, make sure we're dealing with a livemark ID.
PRBool isLivemark = PR_FALSE;
rv = IsLivemark(aContainer, &isLivemark);
NS_ENSURE_SUCCESS(rv, rv);
if (!isLivemark)
return NS_ERROR_INVALID_ARG;
// Now convert the container ID to a container URI for annotation operations
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_FAILURE);
nsCOMPtr<nsIURI> containerURI;
rv = bookmarks->GetFolderURI(aContainer, getter_AddRefs(containerURI));
NS_ENSURE_SUCCESS(rv, rv);
if (!aSiteURI) { // clear any existing site URI
rv = mAnnotationService->RemoveAnnotation(
containerURI, NS_LITERAL_CSTRING(LMANNO_SITEURI));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsCAutoString siteURISpec;
rv = aSiteURI->GetSpec(siteURISpec);
NS_ENSURE_SUCCESS(rv, rv);
rv = mAnnotationService->SetAnnotationString(
containerURI,
NS_LITERAL_CSTRING(LMANNO_SITEURI),
NS_ConvertUTF8toUTF16(siteURISpec),
0,
nsIAnnotationService::EXPIRE_NEVER);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
nsLivemarkService::GetFeedURI(PRInt64 aContainer, nsIURI **aURI)
{
nsresult rv;
// Convert the container ID to a container URI for annotation operations
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_FAILURE);
nsCOMPtr<nsIURI> containerURI;
rv = bookmarks->GetFolderURI(aContainer, getter_AddRefs(containerURI));
NS_ENSURE_SUCCESS(rv, rv);
// If there's no feed URI annotation, that means this isn't a livemark
nsAutoString feedURIString;
rv = mAnnotationService->GetAnnotationString(
containerURI, NS_LITERAL_CSTRING(LMANNO_FEEDURI), feedURIString);
if (NS_FAILED(rv))
return NS_ERROR_INVALID_ARG;
nsCOMPtr<nsIURI> feedURI;
rv = NS_NewURI(getter_AddRefs(feedURI), NS_ConvertUTF16toUTF8(feedURIString));
NS_ENSURE_SUCCESS(rv, rv);
*aURI = feedURI.get();
NS_IF_ADDREF(*aURI);
return NS_OK;
}
NS_IMETHODIMP
nsLivemarkService::SetFeedURI(PRInt64 aContainer, nsIURI *aFeedURI)
{
nsresult rv;
if (!aFeedURI) // a livemark folder with no feed URI is an illegal state
return NS_ERROR_NULL_POINTER;
// Convert the container ID to a container URI for annotation operations
nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
NS_ENSURE_TRUE(bookmarks, NS_ERROR_FAILURE);
nsCOMPtr<nsIURI> containerURI;
rv = bookmarks->GetFolderURI(aContainer, getter_AddRefs(containerURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString feedURISpec;
rv = aFeedURI->GetSpec(feedURISpec);
NS_ENSURE_SUCCESS(rv, rv);
rv = mAnnotationService->SetAnnotationString(
containerURI,
NS_LITERAL_CSTRING(LMANNO_FEEDURI),
NS_ConvertUTF8toUTF16(feedURISpec),
0,
nsIAnnotationService::EXPIRE_NEVER);
NS_ENSURE_SUCCESS(rv, rv);
// Now update our internal table
PRInt32 livemarkIndex = GetLivemarkIndex(aContainer);
NS_ENSURE_TRUE(livemarkIndex > -1, NS_ERROR_FAILURE);
mLivemarks[livemarkIndex]->feedURI = aFeedURI;
return NS_OK;
}
#if 0
// These two container API functions are not currently in the remote container
// service because they are untested. We do not need them here.
NS_IMETHODIMP
nsLivemarkService::OnContainerOpening(
nsINavHistoryContainerResultNode* container,
nsINavHistoryQueryOptions* options)
{
// Nothing to do here since the containers are filled in
// as the livemarks are loaded through FireTimer().
return NS_OK;
}
NS_IMETHODIMP
nsLivemarkService::OnContainerClosed(nsINavHistoryContainerResultNode* container)
{
// Nothing to clean up
return NS_OK;
}
#endif
NS_IMETHODIMP
nsLivemarkService::OnContainerRemoving(PRInt64 aContainer)
{
nsresult rv;
// Find the livemark id in the list of livemarks.
PRInt32 lmIndex = -1;
PRInt32 i;
for (i = 0; i < PRInt32(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
LivemarkInfo *removedItem = mLivemarks[lmIndex];
rv = mAnnotationService->RemoveAnnotation(removedItem->folderURI,
NS_LITERAL_CSTRING(LMANNO_FEEDURI));
NS_ENSURE_SUCCESS(rv, rv);
rv = mAnnotationService->RemoveAnnotation(removedItem->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 < PRInt32(mLivemarks.Length()); i++) {
if (i != lmIndex &&
(NS_OK == mLivemarks[i]->feedURI->Equals(removedItem->feedURI, &urisEqual)) &&
urisEqual) {
stillInUse = PR_TRUE;
break;
}
}
if (!stillInUse) {
// No other livemarks use this feed. Clear all the annotations for it.
rv = mAnnotationService->RemoveAnnotation(removedItem->feedURI,
NS_LITERAL_CSTRING("livemark_expiration"));
NS_ENSURE_SUCCESS(rv, rv);
}
// Cancel the load before we remove the element; this will ensure that
// a LivemarkLoadListener won't try to add items for this load.
if (removedItem->loadGroup) {
removedItem->loadGroup->Cancel(NS_BINDING_ABORTED);
}
// Take the annotation out of the list of annotations.
mLivemarks.RemoveElementAt(lmIndex);
// Get rid of the children for this feed, clearing their annotations.
DeleteLivemarkChildren(aContainer);
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 < PRInt32(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);
// rv will be failure if no site URI annotation is present
if (NS_SUCCEEDED(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);
}
return NS_OK;
}
NS_IMETHODIMP
nsLivemarkService::GetChildrenReadOnly(PRBool *aResult)
{
*aResult = PR_TRUE;
return NS_OK;
}
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 (PRUint32 i = 0; i < lmks->mLivemarks.Length(); i++) {
lmks->UpdateLivemarkChildren(i, PR_FALSE);
}
}
nsresult
nsLivemarkService::DeleteLivemarkChildren(PRInt64 aLivemarkFolderId)
{
nsresult rv;
nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
nsNavHistory* history = History();
nsCOMPtr<nsINavHistoryQuery> query;
rv = history->GetNewQuery(getter_AddRefs(query));
NS_ENSURE_TRUE(query, NS_ERROR_OUT_OF_MEMORY);
rv = query->SetFolders(&aLivemarkFolderId, 1);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINavHistoryQueryOptions> options;
rv = history->GetNewQueryOptions(getter_AddRefs(options));
NS_ENSURE_TRUE(options, NS_ERROR_OUT_OF_MEMORY);
PRUint32 mode = nsINavHistoryQueryOptions::GROUP_BY_FOLDER;
rv = options->SetGroupingMode(&mode, 1);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINavHistoryResult> result;
rv = history->ExecuteQuery(query, options, getter_AddRefs(result));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINavHistoryQueryResultNode> root;
rv = result->GetRoot(getter_AddRefs(root));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 cc;
root->SetContainerOpen(PR_TRUE);
rv = root->GetChildCount(&cc);
NS_ENSURE_SUCCESS(rv, rv);
for (PRUint32 i = 0; i < cc; i++) {
nsCOMPtr<nsINavHistoryResultNode> node;
rv = root->GetChild(i, getter_AddRefs(node));
if (NS_FAILED(rv)) continue;
nsCAutoString spec;
rv = node->GetUri(spec);
if (NS_FAILED(rv)) continue;
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), spec, nsnull);
if (NS_FAILED(rv)) continue;
rv = mAnnotationService->RemoveAnnotation(uri,
NS_LITERAL_CSTRING(LMANNO_BMANNO));
if (NS_FAILED(rv)) continue;
}
// Get the folder children.
rv = bookmarks->RemoveFolderChildren(aLivemarkFolderId);
return rv;
}
nsresult
nsLivemarkService::InsertLivemarkChild(PRInt64 aLivemarkFolderId,
nsIURI *aURI,
const nsAString &aTitle,
const nsAString &aFeedURI)
{
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);
mAnnotationService->SetAnnotationString(aURI,
NS_LITERAL_CSTRING(LMANNO_BMANNO),
aFeedURI,
0,
nsIAnnotationService::EXPIRE_NEVER);
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;
}
NS_IMETHODIMP
nsLivemarkService::ReloadAllLivemarks()
{
for (PRUint32 i = 0; i < mLivemarks.Length(); i++) {
UpdateLivemarkChildren(i, PR_TRUE);
}
return NS_OK;
}
NS_IMETHODIMP
nsLivemarkService::ReloadLivemarkFolder(PRInt64 aFolderId)
{
PRInt32 folderIndex = GetLivemarkIndex(aFolderId);
if (folderIndex == -1)
return NS_ERROR_INVALID_ARG;
return UpdateLivemarkChildren(folderIndex, PR_TRUE);
}
// Returns the index into mLivemarks that corresponds to the given
// folder ID, or -1 if not found.
PRInt32
nsLivemarkService::GetLivemarkIndex(PRInt64 aFolderId)
{
for (PRUint32 i = 0; i < mLivemarks.Length(); i++) {
if (mLivemarks[i]->folderId == aFolderId) {
return i;
}
}
return -1;
}

View File

@ -1,170 +0,0 @@
/* -*- 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 "nsToolkitCompsCID.h"
#include "nsILoadGroup.h"
#include "nsIObserver.h"
/**
* This annotation's value is a string containing the URI of the
* source feed for the given livemark. It should be attached to the URI
* of the livemark container.
*/
#define LMANNO_FEEDURI "livemark/feedURI"
/**
* This annotation's value is a string containing the URI of the
* website associated with the source feed for the given livemark.
* It should be attached to the URI of the livemark container.
*/
#define LMANNO_SITEURI "livemark/siteURI"
/**
* This annotation's value is an Int64 representing the time
* (in milliseconds since January 1, 1970 GMT) when a
* livemark feed should expire and thus have its associated livemark
* refreshed. It should be attached to the URI of the livemark's
* source feed.
*/
#define LMANNO_EXPIRATION "livemark/expiration"
/**
* This annotation's value is a string containing the URI of the
* syndication feed which was this source for a livemark item.
* It should be attached to the URI of a livemark item (that is, a
* child of a livemark container).
*/
#define LMANNO_BMANNO "livemark/bookmarkFeedURI"
class nsIRDFResource;
class nsLivemarkService : public nsILivemarkService, public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIREMOTECONTAINER
NS_DECL_NSILIVEMARKSERVICE
NS_DECL_NSIOBSERVER
nsLivemarkService();
nsresult Init();
static nsLivemarkService* GetLivemarkService() {
if (!sInstance) {
nsresult rv;
nsCOMPtr<nsILivemarkService> serv(do_GetService(NS_LIVEMARKSERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, nsnull);
NS_ASSERTION(sInstance, "Should have static instance pointer now");
}
return sInstance;
}
// 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,
const nsAString &aFeedURI);
nsresult InsertLivemarkLoadingItem(PRInt64 aFolder);
nsresult InsertLivemarkFailedItem(PRInt64 aFolder);
struct LivemarkInfo {
PRInt64 folderId;
nsCOMPtr<nsIURI> folderURI;
nsCOMPtr<nsIURI> feedURI;
PRBool locked;
// Keep track of the load group that contains the channel we're using
// to load this livemark. This allows the load to be cancelled if
// necessary. The load group automatically adds redirect channels, so
// cancelling the load group cancels everything.
nsCOMPtr<nsILoadGroup> loadGroup;
LivemarkInfo(PRInt64 aFolderId, nsIURI *aFolderURI, nsIURI *aFeedURI)
: folderId(aFolderId), folderURI(aFolderURI), feedURI(aFeedURI),
locked(PR_FALSE) { }
void AddRef() { ++mRefCnt; }
void Release() { if (--mRefCnt == 0) delete this; }
private:
nsAutoRefCnt mRefCnt;
};
nsCOMPtr<nsIRDFResource> mLMRDF_type;
nsCOMPtr<nsIRDFResource> mLMRSS09_channel;
nsCOMPtr<nsIRDFResource> mLMRSS09_item;
nsCOMPtr<nsIRDFResource> mLMRSS09_title;
nsCOMPtr<nsIRDFResource> mLMRSS09_link;
nsCOMPtr<nsIRDFResource> mLMRSS10_channel;
nsCOMPtr<nsIRDFResource> mLMRSS10_items;
nsCOMPtr<nsIRDFResource> mLMRSS10_title;
nsCOMPtr<nsIRDFResource> mLMRSS10_link;
nsCOMPtr<nsIRDFResource> mLMDC_date;
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<nsIURI> mIconURI;
nsCOMPtr<nsIAnnotationService> mAnnotationService;
// The list of livemarks is stored in this array
nsTArray< nsRefPtr<LivemarkInfo> > mLivemarks;
// Livemarks are updated on a timer.
nsCOMPtr<nsITimer> mTimer;
static void FireTimer(nsITimer* aTimer, void* aClosure);
nsresult UpdateLivemarkChildren(PRInt32 aLivemarkIndex, PRBool aForceUpdate);
PRInt32 GetLivemarkIndex(PRInt64 folderID);
};

View File

@ -0,0 +1,692 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* The Original Code is the Places JS Livemark Service.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Annie Sullivan <annie.sullivan@gmail.com> (C++ author)
* Joe Hughes <joe@retrovirus.com>
* Vladimir Vukicevic <vladimir@pobox.com>
* Masayuki Nakano <masayuki@d-toybox.com>
* Robert Sayre <sayrer@gmail.com> (JS port)
*
* 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 ***** */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
#include ../../url-classifier/content/js/lang.js
#include ../../url-classifier/content/moz/lang.js
#include ../../url-classifier/content/moz/observer.js
#include ../../url-classifier/content/moz/alarm.js
function LOG(str) {
dump("*** " + str + "\n");
}
const LS_CLASSID = Components.ID("{dca61eb5-c7cd-4df1-b0fb-d0722baba251}");
const LS_CLASSNAME = "Livemark Service";
const LS_CONTRACTID = "@mozilla.org/browser/livemark-service;2";
const LIVEMARK_TIMEOUT = 15000; // fire every 15 seconds
const LIVEMARK_ICON_URI = "chrome://browser/skin/places/livemarkItem.png";
const PLACES_BUNDLE_URI =
"chrome://browser/locale/places/places.properties";
const DEFAULT_LOAD_MSG = "Live Bookmark loading...";
const DEFAULT_FAIL_MSG = "Live Bookmark feed failed to load.";
const LMANNO_FEEDURI = "livemark/feedURI";
const LMANNO_SITEURI = "livemark/siteURI";
const LMANNO_EXPIRATION = "livemark/expiration";
const LMANNO_BMANNO = "livemark/bookmarkFeedURI";
const NH_CONTRACTID = "@mozilla.org/browser/nav-history-service;1";
const AS_CONTRACTID = "@mozilla.org/browser/annotation-service;1";
const OS_CONTRACTID = "@mozilla.org/observer-service;1";
const SB_CONTRACTID = "@mozilla.org/intl/stringbundle;1";
const IO_CONTRACTID = "@mozilla.org/network/io-service;1";
const BMS_CONTRACTID = "@mozilla.org/browser/nav-bookmarks-service;1";
const FAV_CONTRACTID = "@mozilla.org/browser/favicon-service;1";
const LG_CONTRACTID = "@mozilla.org/network/load-group;1";
const FP_CONTRACTID = "@mozilla.org/feed-processor;1";
const SEC_CONTRACTID = "@mozilla.org/scriptsecuritymanager;1";
const SEC_FLAGS = Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT_OR_DATA;
// Check every 1/2 hour
const EXPIRATION = 1800000;
// Check every 10 minutes on error
const ERROR_EXPIRATION = 600000;
var gIoService = Cc[IO_CONTRACTID].getService(Ci.nsIIOService);
var gStringBundle;
function GetString(name)
{
try {
if (!gStringBundle) {
var bundleService = Cc[SB_CONTRACTID].getService();
bundleService = bundleService.QueryInterface(Ci.nsIStringBundleService);
gStringBundle = bundleService.createBundle(PLACES_BUNDLE_URI);
}
if (gStringBundle)
return gStringBundle.GetStringFromName(name);
} catch (ex) {
LOG("Exception loading string bundle: " + ex.message);
}
return null;
}
var gLivemarkService;
function LivemarkService() {
// [ {folderId:, folderURI:, feedURI:, loadGroup:, locked: } ];
this._livemarks = [];
this._iconURI = gIoService.newURI(LIVEMARK_ICON_URI, null, null);
this._loading = GetString("bookmarksLivemarkLoading") || DEFAULT_LOAD_MSG;
this._observerServiceObserver =
new G_ObserverServiceObserver('xpcom-shutdown',
BindToObject(this._shutdown, this),
true /*only once*/);
new G_Alarm(BindToObject(this._fireTimer, this), LIVEMARK_TIMEOUT,
true /* repeat */);
// this is giving a reentrant getService warning in XPCShell. bug 194568.
this._ans = Cc[AS_CONTRACTID].getService(Ci.nsIAnnotationService);
var livemarks = this._ans.getPagesWithAnnotation(LMANNO_FEEDURI, {});
for (var i = 0; i < livemarks.length; i++) {
var feedURI =
gIoService.newURI(
this._ans.getAnnotationString(livemarks[i], LMANNO_FEEDURI),
null, null
);
var queries = { }, options = { };
this._history.queryStringToQueries(livemarks[i].spec, queries, {}, options);
var count = {};
var folders = queries.value[0].getFolders(count);
if (!(queries.value.length && queries.value.length == 1) &&
count.value != 1)
continue; // invalid folder URI (should identify exactly one folder)
this._pushLivemark(folders[0], livemarks[i], feedURI);
}
}
LivemarkService.prototype = {
get _bms() {
if (!this.__bms)
this.__bms = Cc[BMS_CONTRACTID].getService(Ci.nsINavBookmarksService);
return this.__bms;
},
get _history() {
if (!this.__history)
this.__history = Cc[NH_CONTRACTID].getService(Ci.nsINavHistoryService);
return this.__history;
},
// returns new length of _livemarks
_pushLivemark: function LS__pushLivemark(folderId, folderURI, feedURI) {
return this._livemarks.push({folderId: folderId, folderURI: folderURI,
feedURI: feedURI});
},
_getLivemarkIndex: function LS__getLivemarkIndex(folderId) {
for (var i=0; i < this._livemarks.length; ++i) {
if (this._livemarks[i].folderId == folderId)
return i;
}
throw Cr.NS_ERROR_INVALID_ARG;
},
_shutdown: function LS__shutdown() {
for (var livemark in this._livemarks) {
if (livemark.loadGroup)
livemark.loadGroup.cancel(Cr.NS_BINDING_ABORTED);
}
},
_fireTimer: function LS__fireTimer() {
for (var i=0; i < this._livemarks.length; ++i) {
this._updateLivemarkChildren(i, false);
}
},
deleteLivemarkChildren: function LS_deleteLivemarkChildren(folderId) {
var query = this._history.getNewQuery();
query.setFolders([folderId], 1);
var options = this._history.getNewQueryOptions();
options.setGroupingMode([Ci.nsINavHistoryQueryOptions.GROUP_BY_FOLDER], 1);
var result = this._history.executeQuery(query, options);
var root = result.root;
root.containerOpen = true;
var cc = root.childCount;
for (var i=0; i < cc; ++i) {
try {
var node = root.getChild(i);
this._ans.removeAnnotation(gIoService.newURI(node.uri, null, null),
LMANNO_BMANNO);
}
catch (ex) {
// continue
}
}
this._bms.removeFolderChildren(folderId);
},
insertLivemarkLoadingItem: function LS_insertLivemarkLoading(folderId) {
var loadingURI = gIoService.newURI("about:livemark-loading", null, null);
this._bms.insertItem(folderId, loadingURI, -1);
this._bms.setItemTitle(loadingURI, this._loading);
},
_updateLivemarkChildren:
function LS__updateLivemarkChildren(index, forceUpdate) {
if (this._livemarks[index].locked)
return;
var livemark = this._livemarks[index];
livemark.locked = true;
try {
// Check the TTL/expiration on this. If there isn't one,
// then we assume it's never been loaded. We perform this
// check even when the update is being forced, in case the
// livemark has somehow never been loaded.
var exprTime = this._ans.getAnnotationInt64(livemark.feedURI,
LMANNO_EXPIRATION);
if (!forceUpdate && exprTime > Date.now()) {
// no need to refresh
livemark.locked = false;
return;
}
}
catch (ex) {
// This livemark has never been loaded, since it has no expire time.
this.insertLivemarkLoadingItem(livemark.folderId);
}
var loadgroup;
try {
// Create a load group for the request. This will allow us to
// automatically keep track of redirects, so we can always
// cancel the channel.
loadgroup = Cc[LG_CONTRACTID].createInstance(Ci.nsILoadGroup);
var uriChannel = gIoService.newChannel(livemark.feedURI.spec, null, null);
uriChannel.loadGroup = loadgroup;
uriChannel.loadFlags |= Ci.nsIRequest.LOAD_BACKGROUND |
Ci.nsIRequest.VALIDATE_ALWAYS;
var httpChannel = uriChannel.QueryInterface(Ci.nsIHttpChannel);
httpChannel.requestMethod = "GET";
httpChannel.setRequestHeader("X-Moz", "livebookmarks", false);
// Stream the result to the feed parser with this listener
var listener = new LivemarkLoadListener(livemark);
httpChannel.asyncOpen(listener, null);
}
catch (ex) {
livemark.locked = false;
LOG("exception: " + ex);
throw ex;
}
livemark.loadGroup = loadgroup;
},
createLivemark: function LS_createLivemark(folder, name, siteURI,
feedURI, index) {
// Don't add livemarks to livemarks
if (this.isLivemark(folder))
throw Cr.NS_ERROR_INVALID_ARG;
var livemark = this._bms.createContainer(folder, name, LS_CONTRACTID, index);
var livemarkURI = this._bms.getFolderURI(livemark);
// Add an annotation to map the folder URI to the livemark feed URI
this._ans.setAnnotationString(livemarkURI, LMANNO_FEEDURI, feedURI.spec, 0,
this._ans.EXPIRE_NEVER);
// Set the favicon
var faviconService = Cc[FAV_CONTRACTID].getService(Ci.nsIFaviconService);
faviconService.setFaviconUrlForPage(livemarkURI, this._iconURI);
if (siteURI) {
// Add an annotation to map the folder URI to the livemark site URI
this._ans.setAnnotationString(livemarkURI, LMANNO_SITEURI, siteURI.spec,
0, this._ans.EXPIRE_NEVER);
}
this._updateLivemarkChildren(
this._pushLivemark(livemark, livemarkURI, feedURI) - 1,
false
);
return livemark;
},
isLivemark: function LS_isLivemark(folder) {
var folderURI = this._bms.getFolderURI(folder);
return this._ans.hasAnnotation(folderURI, LMANNO_FEEDURI);
},
_ensureLivemark: function LS__ensureLivemark(container) {
if (!this.isLivemark(container))
throw Cr.NS_ERROR_INVALID_ARG;
},
getSiteURI: function LS_getSiteURI(container) {
this._ensureLivemark(container);
var containerURI = this._bms.getFolderURI(container);
var siteURIString;
try {
siteURIString =
this._ans.getAnnotationString(containerURI, LMANNO_SITEURI);
}
catch (ex) {
return null;
}
return gIoService.newURI(siteURIString, null, null);
},
setSiteURI: function LS_setSiteURI(container, siteURI) {
this._ensureLivemark(container);
var containerURI = this._bms.getFolderURI(container);
if (!siteURI) {
this._ans.removeAnnotation(containerURI, LMANNO_SITEURI);
return;
}
this._ans.setAnnotationString(containerURI, LMANNO_SITEURI, siteURI.spec,
0, this._ans.EXPIRE_NEVER);
},
getFeedURI: function LS_getFeedURI(container) {
var containerURI = this._bms.getFolderURI(container);
return gIoService.newURI(this._ans.getAnnotationString(containerURI,
LMANNO_FEEDURI),
null, null);
},
setFeedURI: function LS_setFeedURI(container, feedURI) {
if (!feedURI)
throw Cr.NS_ERROR_INVALID_ARG;
var containerURI = this._bms.getFolderURI(container);
this._ans.setAnnotationString(containerURI, LMANNO_FEEDURI, feedURI.spec,
0, this._ans.EXPIRE_NEVER);
// now update our internal table
var livemarkIndex = this._getLivemarkIndex(container);
this._livemarks[livemarkIndex].feedURI = feedURI;
},
reloadAllLivemarks: function LS_reloadAllLivemarks() {
for (var i = 0; i < this._livemarks.length; ++i) {
this._updateLivemarkChildren(i, true);
}
},
reloadLivemarkFolder: function LS_reloadLivemarkFolder(folderID) {
var livemarkIndex = this._getLivemarkIndex(folderID);
this._updateLivemarkChildren(livemarkIndex, true);
},
// nsIRemoteContainer
onContainerRemoving: function LS_onContainerRemoving(container) {
var livemarkIndex = this._getLivemarkIndex(container);
var livemark = this._livemarks[livemarkIndex];
// Remove the annotations that link the folder URI to the
// Feed URI and Site URI
this._ans.removeAnnotation(livemark.folderURI, LMANNO_FEEDURI);
this._ans.removeAnnotation(livemark.folderURI, LMANNO_SITEURI);
var stillInUse = false;
stillInUse = this._livemarks.some(
function(mark) { return mark.feedURI.equals(livemark.feedURI) }
);
if (!stillInUse) {
// ??? the code in the C++ had "livemark_expiration" as
// the second arg... that must be wrong
this._ans.removeAnnotation(livemark.feedURI, LMANNO_EXPIRATION);
}
if (livemark.loadGroup)
livemark.loadGroup.cancel(Cr.NS_BINDING_ABORTED);
this._livemarks.splice(livemarkIndex, 1);
this.deleteLivemarkChildren(container);
},
// XXXdietrich seems like the mutability of "place:" URIs is the only reason
// this code has to exist, which is kinda high-maintenance.
onContainerMoved:
function LS_onContainerMoved(container, newFolder, newIndex) {
var index = this._getLivemarkIndex(container);
// Update the annotation that maps the folder URI to the livemark feed URI
var newURI = this._bms.getFolderURI(container);
var oldURI = this._livemarks[index].folderURI;
var feedURIString = this._ans.getAnnotationString(oldURI, LMANNO_FEEDURI);
this._ans.removeAnnotation(oldURI, LMANNO_FEEDURI);
this._ans.setAnnotation(newURI, LMANNO_FEEDURI, feedURIString, 0,
this._ans.EXPIRE_NEVER);
// Update the annotation that maps the folder URI to the livemark site URI
var siteURIString;
try {
siteURIString = this._ans.GetAnnotationString(oldURI, LMANNO_SITEURI);
}
catch (ex) {
// will throw if no annotation
}
if (siteURIString) {
this._ans.removeAnnotation(oldURI, LMANNO_SITEURI);
this._ans.setAnnotation(newURI, LMANNO_SITEURI, siteURIString, 0,
this._ans.EXPIRE_NEVER);
}
},
childrenReadOnly: false,
createInstance: function LS_createInstance(outer, iid) {
if (outer != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
return this.QueryInterface(iid);
},
QueryInterface: function LS_QueryInterface(iid) {
if (iid.equals(Ci.nsILivemarkService) ||
iid.equals(Ci.nsIRemoteContainer) ||
iid.equals(Ci.nsIFactory) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
};
function LivemarkLoadListener(livemark) {
this._livemark = livemark;
this._processor = null;
this._isAborted = false;
this._ttl = EXPIRATION;
this._ans = Cc[AS_CONTRACTID].getService(Ci.nsIAnnotationService);
}
LivemarkLoadListener.prototype = {
get _failed() {
return GetString("bookmarksLivemarkFailed") || DEFAULT_FAIL_MSG;
},
abort: function LLL_abort() {
this._isAborted = true;
},
get _bms() {
if (!this.__bms)
this.__bms = Cc[BMS_CONTRACTID].getService(Ci.nsINavBookmarksService);
return this.__bms;
},
get _history() {
if (!this.__history)
this.__history = Cc[NH_CONTRACTID].getService(Ci.nsINavHistoryService);
return this.__history;
},
/**
* See nsIFeedResultListener.idl
*/
handleResult: function LLL_handleResult(result) {
if (this._isAborted) {
this._livemark.locked = false;
return;
}
try {
// We need this to make sure the item links are safe
var secMan = Cc[SEC_CONTRACTID].getService(Ci.nsIScriptSecurityManager);
// Clear out any child nodes of the livemark folder, since
// they're about to be replaced.
var lmService = Cc[LS_CONTRACTID].getService(Ci.nsILivemarkService);
this.deleteLivemarkChildren(this._livemark.folderId);
// Enforce well-formedness because the existing code does
if (!result || !result.doc || result.bozo) {
this.insertLivemarkFailedItem(this._livemark.folderId);
this._ttl = ERROR_EXPIRATION;
throw Cr.NS_ERROR_FAILURE;
}
var title, href, entry;
var feed = result.doc.QueryInterface(Ci.nsIFeed);
// Loop through and check for a link and a title
// as the old code did
for (var i = 0; i < feed.items.length; ++i) {
entry = feed.items.queryElementAt(i, Ci.nsIFeedEntry);
if (entry.title)
title = entry.title.plainText();
else if (entry.updated)
title = entry.updated;
if (entry.link) {
try {
secMan.checkLoadURIStr(this._livemark.feedURI.spec,
entry.link.spec, SEC_FLAGS);
href = entry.link;
}
catch (ex) {
}
}
if (href && title) {
this.insertLivemarkChild(this._livemark.folderId,
href, title);
}
}
}
finally {
this._processor.listener = null;
this._processor = null;
this._livemark.locked = false;
}
},
deleteLivemarkChildren: LivemarkService.prototype.deleteLivemarkChildren,
insertLivemarkFailedItem: function LS_insertLivemarkFailed(folderId) {
var failedURI = gIoService.newURI("about:livemark-failed", null, null);
this._bms.insertItem(folderId, failedURI, -1);
this._bms.setItemTitle(failedURI, this._failed);
},
insertLivemarkChild:
function LS_insertLivemarkChild(folderId, uri, title) {
this._bms.insertItem(folderId, uri, -1);
this._bms.setItemTitle(uri, title);
this._ans.setAnnotationString(uri, LMANNO_BMANNO, uri.spec, 0,
this._ans.EXPIRE_NEVER);
},
/**
* See nsIStreamListener.idl
*/
onDataAvailable: function LLL_onDataAvailable(request, context, inputStream,
sourceOffset, count) {
this._processor.onDataAvailable(request, context, inputStream,
sourceOffset, count);
},
/**
* See nsIRequestObserver.idl
*/
onStartRequest: function LLL_onStartRequest(request, context) {
if (this._isAborted)
throw Cr.NS_ERROR_UNEXPECTED;
var channel = request.QueryInterface(Ci.nsIChannel);
// Parse feed data as it comes in
this._processor = Cc[FP_CONTRACTID].createInstance(Ci.nsIFeedProcessor);
this._processor.listener = this;
this._processor.parseAsync(null, channel.URI);
this._processor.onStartRequest(request, context);
},
/**
* See nsIRequestObserver.idl
*/
onStopRequest: function LLL_onStopReqeust(request, context, status) {
if (!Components.isSuccessCode(status)) {
// Something went wrong; try to load again in a bit
this._setResourceTTL(ERROR_EXPIRATION);
this._isAborted = true;
return;
}
// Set an expiration on the livemark, for reloading the data
try {
this._processor.onStopRequest(request, context, status);
// Calculate a new ttl
var channel = request.QueryInterface(Ci.nsICachingChannel);
if (channel) {
var entryInfo = channel.cacheToken.QueryInterface(Ci.nsICacheEntryInfo);
if (entryInfo) {
var exprtime = entryInfo.expirationTime;
var nowtime = Date.now();
if (nowtime >= exprtime) {
expiresTime -= nowtime;
if (expiresTime > EXPIRATION)
this._setResourceTTL(expiresTime);
else
this._setResourceTTL(EXPIRATION);
}
}
}
}
catch (ex) {
this._setResourceTTL(this._ttl);
}
},
_setResourceTTL: function LLL__setResourceTTL(seconds) {
var exptime = Date.now() + seconds;
this._ans.setAnnotationInt64(this._livemark.feedURI,
LMANNO_EXPIRATION, exptime, 0,
Ci.nsIAnnotationService.EXPIRE_NEVER);
},
/**
* See nsISupports.idl
*/
QueryInterface: function LLL_QueryInterface(iid) {
if (iid.equals(Ci.nsIFeedResultListener) ||
iid.equals(Ci.nsIStreamListener) ||
iid.equals(Ci.nsIRequestObserver)||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
}
function GenericComponentFactory(ctor) {
this._ctor = ctor;
}
GenericComponentFactory.prototype = {
_ctor: null,
// nsIFactory
createInstance: function(outer, iid) {
if (outer != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
return (new this._ctor()).QueryInterface(iid);
},
// nsISupports
QueryInterface: function(iid) {
if (iid.equals(Ci.nsIFactory) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
};
var Module = {
QueryInterface: function(iid) {
if (iid.equals(Ci.nsIModule) ||
iid.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
getClassObject: function M_getClassObject(cm, cid, iid) {
if (!iid.equals(Ci.nsIFactory))
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
if (cid.equals(LS_CLASSID))
return new GenericComponentFactory(LivemarkService);
throw Cr.NS_ERROR_NO_INTERFACE;
},
registerSelf: function(cm, file, location, type) {
var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
cr.registerFactoryLocation(LS_CLASSID, LS_CLASSNAME,
LS_CONTRACTID, file, location, type);
},
unregisterSelf: function M_unregisterSelf(cm, location, type) {
var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
cr.unregisterFactoryLocation(LS_CLASSID, location);
},
canUnload: function M_canUnload(cm) {
return true;
}
};
function NSGetModule(cm, file) {
return Module;
}

View File

@ -5,7 +5,6 @@
#include "nsNavHistory.h"
#include "nsNavBookmarks.h"
#include "nsFaviconService.h"
#include "nsLivemarkService.h"
#include "nsMorkHistoryImporter.h"
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNavHistory, Init)
@ -14,7 +13,6 @@ 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)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMorkHistoryImporter)
static const nsModuleComponentInfo components[] =
@ -59,11 +57,6 @@ static const nsModuleComponentInfo components[] =
NS_FAVICONSERVICE_CONTRACTID,
nsFaviconServiceConstructor },
{ "Livemark Service",
NS_LIVEMARKSERVICE_CID,
NS_LIVEMARKSERVICE_CONTRACTID,
nsLivemarkServiceConstructor },
{ "Mork History Importer",
NS_MORKHISTORYIMPORTER_CID,
NS_MORKHISTORYIMPORTER_CONTRACTID,