fix for #64060 - bulk delete history entries by hostname & domainname

also add a new history controller for future history coolness
a=ben, r=jag, and a bit of r=timeless
This commit is contained in:
alecf%netscape.com 2001-01-31 20:21:37 +00:00
Родитель 9adc8a60f5
Коммит 369607b9ae
9 изменённых файлов: 248 добавлений и 19 удалений

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

@ -44,6 +44,11 @@ interface nsIGlobalHistory : nsISupports
// Remove the specified page from the global history
void RemovePage(in string aURL);
// Remove all pages from the given host.
// If aEntireDomain is true, will assume aHost is a domain,
// and remove all pages from the entire domain.
void RemovePagesFromHost(in string aHost, in boolean aEntireDomain);
// Remove all pages from global history
void RemoveAllPages();

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

@ -44,6 +44,11 @@ interface nsIGlobalHistory : nsISupports
// Remove the specified page from the global history
void RemovePage(in string aURL);
// Remove all pages from the given host.
// If aEntireDomain is true, will assume aHost is a domain,
// and remove all pages from the entire domain.
void RemovePagesFromHost(in string aHost, in boolean aEntireDomain);
// Remove all pages from global history
void RemoveAllPages();

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

@ -23,19 +23,150 @@
// The history window uses JavaScript in bookmarks.js too.
function HistoryInit() {
var tree = document.getElementById("historyTree");
var historyController = new nsTreeController(tree);
function debug(msg)
{
// Uncomment for noise
//dump(msg+"\n");
}
var gHistoryTree;
var gLastHostname;
var gLastDomain;
var gGlobalHistory;
var gDeleteByHostname;
var gDeleteByDomain;
var gHistoryBundle;
function HistoryInit() {
gHistoryTree = document.getElementById("historyTree");
gDeleteByHostname = document.getElementById("cmd_deleteByHostname");
gDeleteByDomain = document.getElementById("cmd_deleteByDomain");
gHistoryBundle = document.getElementById("historyBundle");
var treeController = new nsTreeController(gHistoryTree);
var historyController = new nsHistoryController;
gHistoryTree.controllers.appendController(historyController);
gGlobalHistory = Components.classes["@mozilla.org/browser/global-history;1"].getService(Components.interfaces.nsIGlobalHistory);
var children = document.getElementById('treechildren-bookmarks');
if (children.firstChild)
tree.selectItem(children.firstChild);
tree.focus();
gHistoryTree.selectItem(children.firstChild);
gHistoryTree.focus();
dump("Updating sort..\n");
// do a sort
RefreshSort();
}
function updateHistoryCommands()
{
dump("Updating history commands..\n");
goUpdateCommand("cmd_deleteByHostname");
goUpdateCommand("cmd_deleteByDomain");
}
function historyOnSelect(event)
{
dump("History selection has changed..\n");
// every time selection changes, save the last hostname
gLastHostname = "";
gLastDomain = "";
var selection = gHistoryTree.selectedItems;
if (selection && selection.length > 0) {
var url = selection[0].id;
// matches scheme://(hostname)...
var match = url.match(/.*:\/\/([^\/:]*)/);
if (match && match.length>1)
gLastHostname = match[1];
}
if (gLastHostname) {
// matches the last foo.bar in foo.bar or baz.foo.bar
var match = gLastHostname.match(/([^.]+\.[^.]+$)/);
if (match)
gLastDomain = match[1];
}
document.commandDispatcher.updateCommands("select");
}
function nsHistoryController()
{
}
nsHistoryController.prototype =
{
supportsCommand: function(command)
{
switch(command) {
case "cmd_deleteByHostname":
case "cmd_deleteByDomain":
return true;
default:
return false;
}
},
isCommandEnabled: function(command)
{
var enabled = false;
var stringId;
var text;
switch(command) {
case "cmd_deleteByHostname":
dump("Updating cmd_deleteByHostname..\n");
if (gLastHostname) {
stringId = "deleteHost";
enabled = true;
} else {
stringId = "deleteHostNoSelection";
}
text =
gHistoryBundle.stringBundle.formatStringFromName(stringId,
[ gLastHostname ], 1);
dump("Setting value to " + text + "\n");
gDeleteByHostname.setAttribute("value", text);
break;
case "cmd_deleteByDomain":
if (gLastDomain) {
stringId = "deleteDomain";
enabled = true;
} else {
stringId = "deleteDomainNoSelection";
}
text = gHistoryBundle.stringBundle.formatStringFromName(stringId,
[ gLastDomain ], 1);
gDeleteByDomain.setAttribute("value", text);
}
return enabled;
},
doCommand: function(command)
{
switch(command) {
case "cmd_deleteByHostname":
gGlobalHistory.RemovePagesFromHost(gLastHostname, false)
return true;
case "cmd_deleteByDomain":
gGlobalHistory.RemovePagesFromHost(gLastDomain, true)
return true;
default:
return false;
}
}
}
var historyDNDObserver = {
onDragStart: function (aEvent)
{

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

@ -46,9 +46,16 @@
<script src="chrome://global/content/strres.js"/>
<script src="chrome://global/content/globalOverlay.js"/>
<stringbundle id="historyBundle"
src="chrome://communicator/locale/history/history.properties"/>
<commands id="commands">
<commandset id="selectEditMenuItems"/>
<commandset id="globalEditMenuItems"/>
<commandset id="historyEditMenuItems"
commandupdater="true"
events="select"
oncommandupdate="updateHistoryCommands()"/>
</commands>
<broadcasterset id="broadcasterset">
@ -64,6 +71,8 @@
<broadcaster id="cmd_copy"/>
<broadcaster id="cmd_delete"/>
<broadcaster id="cmd_selectAll"/>
<broadcaster id="cmd_deleteByHostname" oncommand="goDoCommand('cmd_deleteByHostname');"/>
<broadcaster id="cmd_deleteByDomain" oncommand="goDoCommand('cmd_deleteByDomain');"/>
</broadcasterset>
<keyset id="keyset">
<!-- File Menu -->
@ -101,6 +110,8 @@
<menuitem id="menu_cut"/>
<menuitem id="menu_copy"/>
<menuitem id="menu_delete"/>
<menuitem id="menu_deleteByHostname" observes="cmd_deleteByHostname"/>
<menuitem id="menu_deleteByDomain" observes="cmd_deleteByDomain"/>
<menuseparator/>
<menuitem id="menu_selectAll"/>
<menuseparator/>

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

@ -43,6 +43,7 @@
datasources="rdf:history rdf:localsearch"
flex="1"
multiple="true"
onselect="historyOnSelect(event)"
ondraggesture="nsDragAndDrop.startDrag(event, historyDNDObserver);">
<template>

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

@ -0,0 +1,4 @@
deleteHost=Delete all from %S
deleteDomain=Delete entire domain %S
deleteHostNoSelection=Delete host
deleteDomainNoSelection=Delete domain

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

@ -53,6 +53,8 @@
#include "prtime.h"
#include "rdf.h"
#include "nsIURL.h"
#include "nsInt64.h"
#include "nsMorkCID.h"
#include "nsIMdbFactoryFactory.h"
@ -75,9 +77,6 @@ nsIRDFResource* nsGlobalHistory::kNC_HistoryRoot;
nsIRDFResource* nsGlobalHistory::kNC_HistoryBySite;
nsIRDFResource* nsGlobalHistory::kNC_HistoryByDate;
#ifdef DEBUG_sspitzer
#define DEBUG_LAST_PAGE_VISITED 1
#endif /* DEBUG_sspitzer */
#define PREF_BROWSER_HISTORY_LAST_PAGE_VISITED "browser.history.last_page_visited"
#define PREF_BROWSER_HISTORY_EXPIRE_DAYS "browser.history_expire_days"
@ -87,6 +86,7 @@ nsIRDFResource* nsGlobalHistory::kNC_HistoryByDate;
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
static NS_DEFINE_CID(kStandardUrlCID, NS_STANDARDURL_CID);
struct matchExpiration_t {
PRInt64 *expirationDate;
@ -97,6 +97,13 @@ struct matchUrl_t {
const char *url;
};
struct matchHost_t {
const char *host;
PRBool entireDomain; // should we delete the entire domain?
nsGlobalHistory *history;
nsIURL* cachedUrl;
};
static nsresult
PRInt64ToChars(const PRInt64& aValue, char* aBuf, PRInt32 aSize)
{
@ -153,7 +160,7 @@ CharsToPRInt64(const char* aBuf, PRUint32 aCount, PRInt64* aResult)
PRBool
nsGlobalHistory::matchExpiration(nsIMdbRow *row, PRInt64* expirationDate)
nsGlobalHistory::MatchExpiration(nsIMdbRow *row, PRInt64* expirationDate)
{
mdb_err err;
nsresult rv;
@ -174,7 +181,7 @@ static PRBool
matchExpirationCallback(nsIMdbRow *row, void *aClosure)
{
matchExpiration_t *expires = (matchExpiration_t*)aClosure;
return expires->history->matchExpiration(row, expires->expirationDate);
return expires->history->MatchExpiration(row, expires->expirationDate);
}
static PRBool
@ -183,6 +190,12 @@ matchAllCallback(nsIMdbRow *row, void *aClosure)
return PR_TRUE;
}
static PRBool
matchHostCallback(nsIMdbRow *row, void *aClosure)
{
matchHost_t *hostInfo = (matchHost_t*)aClosure;
return hostInfo->history->MatchHost(row, hostInfo);
}
//----------------------------------------------------------------------
nsMdbTableEnumerator::nsMdbTableEnumerator()
@ -686,7 +699,8 @@ nsGlobalHistory::RemovePage(const char *aURL)
{
mdb_err err;
nsresult rv;
if (!mTable) return NS_ERROR_NOT_INITIALIZED;
// find the old row, ignore it if we don't have it
nsMdbPtr<nsIMdbRow> row(mEnv);
rv = FindUrl(aURL, getter_Acquires(row));
@ -709,6 +723,61 @@ nsGlobalHistory::RemovePage(const char *aURL)
return NS_OK;
}
NS_IMETHODIMP
nsGlobalHistory::RemovePagesFromHost(const char *aHost, PRBool aEntireDomain)
{
nsresult rv;
nsCOMPtr<nsIURL> url =
do_CreateInstance(kStandardUrlCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
matchHost_t hostInfo;
hostInfo.history = this;
hostInfo.entireDomain = aEntireDomain;
hostInfo.host = aHost;
hostInfo.cachedUrl = url;
return RemoveMatchingRows(matchHostCallback, (void *)&hostInfo, PR_TRUE);
}
PRBool
nsGlobalHistory::MatchHost(nsIMdbRow *aRow,
matchHost_t *hostInfo)
{
mdb_err err;
nsresult rv;
mdbYarn yarn;
err = aRow->AliasCellYarn(mEnv, kToken_URLColumn, &yarn);
if (err != 0) return PR_FALSE;
// do smart zero-termination
nsLiteralCString url((const char *)yarn.mYarn_Buf, yarn.mYarn_Fill);
rv = hostInfo->cachedUrl->SetSpec(nsPromiseFlatCString(url).get());
if (NS_FAILED(rv)) return PR_FALSE;
nsXPIDLCString urlHost;
rv = hostInfo->cachedUrl->GetHost(getter_Copies(urlHost));
if (NS_FAILED(rv)) return PR_FALSE;
if (PL_strcmp(urlHost, hostInfo->host) == 0)
return PR_TRUE;
// now try for a domain match, if necessary
if (hostInfo->entireDomain) {
// do a reverse-search to match the end of the string
char *domain = PL_strrstr(urlHost, hostInfo->host);
// now verify that we're matching EXACTLY the domain, and
// not some random string inside the hostname
if (domain && (PL_strcmp(domain, hostInfo->host) == 0))
return PR_TRUE;
}
return PR_FALSE;
}
NS_IMETHODIMP
nsGlobalHistory::RemoveAllPages()
{
@ -733,6 +802,8 @@ nsGlobalHistory::RemoveMatchingRows(rowMatchCallback aMatchFunc,
err = mTable->GetCount(mEnv, &count);
if (err != 0) return NS_ERROR_FAILURE;
// XXX tell RDF observers that we're about to do a batch update
// Begin the batch.
int marker;
err = mTable->StartBatchChangeHint(mEnv, &marker);
@ -793,6 +864,9 @@ nsGlobalHistory::RemoveMatchingRows(rowMatchCallback aMatchFunc,
// Finish the batch.
err = mTable->EndBatchChangeHint(mEnv, &marker);
NS_ASSERTION(err == 0, "error ending batch");
// XXX tell RDF observers that we're done with the batch
return ( err == 0) ? NS_OK : NS_ERROR_FAILURE;
}
@ -845,10 +919,6 @@ nsGlobalHistory::SaveLastPageVisited(const char *aURL)
rv = prefs->SetCharPref(PREF_BROWSER_HISTORY_LAST_PAGE_VISITED, aURL);
#ifdef DEBUG_LAST_PAGE_VISITED
printf("XXX saving last page visited as: %s\n", aURL);
#endif /* DEBUG_LAST_PAGE_VISITED */
return rv;
}
@ -868,9 +938,6 @@ nsGlobalHistory::GetLastPageVisited(char **_retval)
*_retval = nsCRT::strdup((const char *)lastPageVisited);
#ifdef DEBUG_LAST_PAGE_VISITED
printf("XXX getting last page visited as: %s\n", (const char *)lastPageVisited);
#endif /* DEBUG_LAST_PAGE_VISITED */
return NS_OK;
}

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

@ -75,6 +75,8 @@ protected:
};
typedef PRBool (*rowMatchCallback)(nsIMdbRow *aRow, void *closure);
struct matchHost_t;
//----------------------------------------------------------------------
//
// nsGlobalHistory
@ -111,7 +113,8 @@ public:
virtual ~nsGlobalHistory();
// these must be public so that the callbacks can call them
PRBool matchExpiration(nsIMdbRow *row, PRInt64* expirationDate);
PRBool MatchExpiration(nsIMdbRow *row, PRInt64* expirationDate);
PRBool MatchHost(nsIMdbRow *row, matchHost_t *hostInfo);
protected:
@ -165,6 +168,7 @@ protected:
nsresult SetRowValue(nsIMdbRow *aRow, mdb_column aCol, const PRUnichar *aValue);
nsresult GetRowValue(nsIMdbRow *aRow, mdb_column aCol, nsAWritableString& aResult);
nsresult GetRowValue(nsIMdbRow *aRow, mdb_column aCol, nsAWritableCString& aResult);
nsresult FindUrl(const char *aURL, nsIMdbRow **aResult);

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

@ -104,6 +104,7 @@ en-US.jar:
locale/en-US/communicator/directory/directory.dtd (directory/locale/en-US/directory.dtd)
locale/en-US/communicator/history/history.dtd (history/resources/locale/en-US/history.dtd)
locale/en-US/communicator/history/historyTreeOverlay.dtd (history/resources/locale/en-US/historyTreeOverlay.dtd)
locale/en-US/communicator/history/history.properties (history/resources/locale/en-US/history.properties)
locale/en-US/communicator/pref/pref-advanced.dtd (prefwindow/resources/locale/en-US/pref-advanced.dtd)
locale/en-US/communicator/pref/pref-appearance.dtd (prefwindow/resources/locale/en-US/pref-appearance.dtd)
locale/en-US/communicator/pref/pref-applications.dtd (prefwindow/resources/locale/en-US/pref-applications.dtd)