Bug 78510. Maintain a per-document hashmap from URI-spec-hash to set of pointers to content elements that are links to those URIs whose state has been queried by the style system. Use this map to efficiently mark visited any links to URIs which get visited. r+sr=dbaron, with input from bryner and bzbarsky, a=jesup with support from dbaron. May impact Tp and other metrics, be careful out there.

This commit is contained in:
roc+%cs.cmu.edu 2005-08-10 20:21:44 +00:00
Родитель 0b742bb6a7
Коммит d187d8344a
21 изменённых файлов: 522 добавлений и 62 удалений

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

@ -740,7 +740,7 @@ function delayedStartup()
gPrefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
BrowserOffline.init();
if (gURLBar && document.documentElement.getAttribute("chromehidden").indexOf("toolbar") != -1) {
gURLBar.setAttribute("readonly", "true");
gURLBar.setAttribute("enablehistory", "false");
@ -4828,8 +4828,8 @@ function asyncOpenWebPanel(event)
// A Web panel's links should target the main content area. Do this
// if no modifier keys are down and if there's no target or the target equals
// _main (the IE convention) or _content (the Mozilla convention).
// The only reason we field _main and _content here is for the markLinkVisited
// hack.
// XXX Now that markLinkVisited is gone, we may not need to field _main and
// _content here.
target = wrapper.getAttribute("target");
var docWrapper = wrapper.ownerDocument;
var locWrapper = docWrapper.location;
@ -4855,7 +4855,6 @@ function asyncOpenWebPanel(event)
var url = getShortcutOrURI(wrapper.href, postData);
if (!url)
return true;
markLinkVisited(wrapper.href, linkNode);
loadURI(url, null, postData.value);
event.preventDefault();
return false;

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

@ -681,8 +681,10 @@ nsSimpleGlobalHistory::AddExistingPageToDatabase(nsIMdbRow *row,
// if the page was typed, unhide it now because it's
// known to be valid
if (HasCell(mEnv, row, kToken_TypedColumn))
if (HasCell(mEnv, row, kToken_TypedColumn)) {
mTypedHiddenURIs.Remove(URISpec);
row->CutColumn(mEnv, kToken_HiddenColumn);
}
// Update last visit date.
// First get the old date so we can update observers...
@ -1413,9 +1415,18 @@ nsSimpleGlobalHistory::IsVisited(nsIURI* aURI, PRBool *_retval)
rv = aURI->GetSpec(URISpec);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMdbRow> row;
rv = FindRow(kToken_URLColumn, URISpec.get(), getter_AddRefs(row));
rv = FindRow(kToken_URLColumn, URISpec.get(), nsnull);
*_retval = NS_SUCCEEDED(rv);
// Hidden, typed URIs haven't really been visited yet. They've only
// been typed in and the actual load hasn't happened yet. We maintain
// the list of hidden+typed URIs in memory in mTypedHiddenURIs because
// the list will usually be small and checking the actual Mork row
// would require several dynamic memory allocations.
if (*_retval && mTypedHiddenURIs.Contains(URISpec))
*_retval = PR_FALSE;
}
*_retval = NS_SUCCEEDED(rv);
return NS_OK;
@ -1582,6 +1593,7 @@ nsSimpleGlobalHistory::MarkPageAsTyped(nsIURI *aURI)
// We don't know if this is a valid URI yet. Hide it until it finishes
// loading.
SetRowValue(row, kToken_HiddenColumn, 1);
mTypedHiddenURIs.Put(spec);
}
rv = SetRowValue(row, kToken_TypedColumn, 1);
@ -1635,7 +1647,9 @@ nsSimpleGlobalHistory::Init()
observerService->AddObserver(this, "profile-before-change", PR_TRUE);
observerService->AddObserver(this, "profile-do-change", PR_TRUE);
}
mTypedHiddenURIs.Init(3);
return NS_OK;
}

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

@ -57,6 +57,7 @@
#include "nsString.h"
#include "nsVoidArray.h"
#include "nsSupportsArray.h"
#include "nsHashSets.h"
struct MatchHostData;
struct SearchQueryData;
@ -307,6 +308,7 @@ protected:
mdb_column kToken_LastPageVisited;
mdb_column kToken_ByteOrder;
nsCStringHashSet mTypedHiddenURIs;
};

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

@ -52,6 +52,8 @@
#include "nsCRT.h"
#include "mozFlushType.h"
#include "nsPropertyTable.h"
#include "nsHashSets.h"
#include "nsAutoPtr.h"
class nsIAtom;
class nsIContent;
@ -90,8 +92,8 @@ class nsILayoutHistoryState;
// IID for the nsIDocument interface
#define NS_IDOCUMENT_IID \
{ 0x9339ff1e, 0xdab0, 0x4264, \
{ 0x8a, 0x8c, 0xcb, 0x84, 0xeb, 0x4e, 0x6b, 0x92 } }
{ 0xd7c47f55, 0x480b, 0x4a60, \
{ 0x9a, 0xdf, 0xca, 0x49, 0x87, 0x3c, 0x71, 0xe2 } }
// The base value for the content ID counter.
// This counter is used by the document to
@ -103,6 +105,7 @@ class nsILayoutHistoryState;
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
#define NS_LINK_VISITED_EVENT_TOPIC "link-visited"
//----------------------------------------------------------------------
@ -751,6 +754,28 @@ public:
* |aPersisted| parameter.
*/
virtual void OnPageHide(PRBool aPersisted) = 0;
/*
* We record the set of links in the document that are relevant to
* style.
*/
/**
* Notification that an element is a link with a given URI that is
* relevant to style.
*/
virtual void AddStyleRelevantLink(nsIContent* aContent, nsIURI* aURI) = 0;
/**
* Notification that an element is a link and its URI might have been
* changed or the element removed. If the element is still a link relevant
* to style, then someone must ensure that AddStyleRelevantLink is
* (eventually) called on it again.
*/
virtual void ForgetLink(nsIContent* aContent) = 0;
/**
* Notification that the visitedness state of a URI has been changed
* and style related to elements linking to that URI should be updated.
*/
virtual void NotifyURIVisitednessChanged(nsIURI* aURI) = 0;
protected:
~nsIDocument()

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

@ -140,6 +140,115 @@ static NS_DEFINE_CID(kDOMEventGroupCID, NS_DOMEVENTGROUP_CID);
static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);
void
nsUint32ToContentHashEntry::Destroy()
{
HashSet* set = GetHashSet();
if (set) {
delete set;
} else {
nsIContent* content = GetContent();
NS_IF_RELEASE(content);
}
}
nsresult
nsUint32ToContentHashEntry::PutContent(nsIContent* aVal)
{
// Add the value to the hash if it is there
HashSet* set = GetHashSet();
if (set) {
nsISupportsHashKey* entry = set->PutEntry(aVal);
return entry ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
// If an element is already there, create a hashtable and both of these to it
if (GetContent()) {
nsIContent* oldVal = GetContent();
nsresult rv = InitHashSet(&set);
NS_ENSURE_SUCCESS(rv, rv);
nsISupportsHashKey* entry = set->PutEntry(oldVal);
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
// The hashset adds its own reference, so release the one we had
NS_RELEASE(oldVal);
entry = set->PutEntry(aVal);
return entry ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
// Nothing exists in the hash right now, so just set the single pointer
return SetContent(aVal);
}
void
nsUint32ToContentHashEntry::RemoveContent(nsIContent* aVal)
{
// Remove from the hash if the hash is there
HashSet* set = GetHashSet();
if (set) {
set->RemoveEntry(aVal);
if (set->Count() == 0) {
delete set;
mValOrHash = nsnull;
}
return;
}
// Remove the ptr if there is just a ptr
nsIContent* v = GetContent();
if (v == aVal) {
NS_IF_RELEASE(v);
mValOrHash = nsnull;
}
}
nsresult
nsUint32ToContentHashEntry::InitHashSet(HashSet** aSet)
{
HashSet* newSet = new HashSet();
if (!newSet) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = newSet->Init();
NS_ENSURE_SUCCESS(rv, rv);
mValOrHash = newSet;
*aSet = newSet;
return NS_OK;
}
static PLDHashOperator PR_CALLBACK
nsUint32ToContentHashEntryVisitorCallback(nsISupportsHashKey* aEntry,
void* aClosure)
{
nsUint32ToContentHashEntry::Visitor* visitor =
NS_STATIC_CAST(nsUint32ToContentHashEntry::Visitor*, aClosure);
visitor->Visit(NS_STATIC_CAST(nsIContent*, aEntry->GetKey()));
return PL_DHASH_NEXT;
}
void
nsUint32ToContentHashEntry::VisitContent(Visitor* aVisitor)
{
HashSet* set = GetHashSet();
if (set) {
set->EnumerateEntries(nsUint32ToContentHashEntryVisitorCallback, aVisitor);
if (set->Count() == 0) {
delete set;
mValOrHash = nsnull;
}
return;
}
nsIContent* v = GetContent();
if (v) {
aVisitor->Visit(v);
}
}
// Helper structs for the content->subdoc map
class SubDocMapEntry : public PLDHashEntryHdr
@ -749,6 +858,8 @@ nsDocument::Init()
return NS_ERROR_ALREADY_INITIALIZED;
}
mLinkMap.Init();
// Force initialization.
nsBindingManager *bindingManager = new nsBindingManager();
NS_ENSURE_TRUE(bindingManager, NS_ERROR_OUT_OF_MEMORY);
@ -4963,6 +5074,9 @@ nsDocument::UnblockOnload()
void
nsDocument::OnPageShow(PRBool aPersisted)
{
mVisible = PR_TRUE;
UpdateLinkMap();
if (aPersisted) {
// Send out notifications that our <link> elements are attached.
nsRefPtr<nsContentList> links = NS_GetContentList(this,
@ -5028,4 +5142,104 @@ nsDocument::OnPageHide(PRBool aPersisted)
NS_EVENT_FLAG_INIT, &status);
}
}
mVisible = PR_FALSE;
}
static PRUint32 GetURIHash(nsIURI* aURI)
{
nsCAutoString str;
aURI->GetSpec(str);
return HashString(str);
}
void
nsDocument::AddStyleRelevantLink(nsIContent* aContent, nsIURI* aURI)
{
nsUint32ToContentHashEntry* entry = mLinkMap.PutEntry(GetURIHash(aURI));
if (!entry) // out of memory?
return;
entry->PutContent(aContent);
}
void
nsDocument::ForgetLink(nsIContent* aContent)
{
nsCOMPtr<nsIURI> uri = nsContentUtils::GetLinkURI(aContent);
if (!uri)
return;
PRUint32 hash = GetURIHash(uri);
nsUint32ToContentHashEntry* entry = mLinkMap.GetEntry(hash);
if (!entry)
return;
entry->RemoveContent(aContent);
if (entry->IsEmpty()) {
// Remove the entry and allow the table to resize, in case
// a lot of links are being removed from the document or modified
mLinkMap.RemoveEntry(hash);
}
}
class URIVisitNotifier : public nsUint32ToContentHashEntry::Visitor
{
public:
nsCAutoString matchURISpec;
nsIDocument* document;
virtual void Visit(nsIContent* aContent) {
// Ensure that the URIs really match before we try to do anything
nsCOMPtr<nsIURI> uri = nsContentUtils::GetLinkURI(aContent);
if (!uri) {
NS_ERROR("Should have found a URI for content in the link map");
return;
}
nsCAutoString spec;
uri->GetSpec(spec);
// We use nsCString::Equals here instead of nsIURI::Equals because
// history matching is all based on spec equality
if (!spec.Equals(matchURISpec))
return;
// Throw away the cached link state so it gets refetched by the style
// system
nsCOMPtr<nsILink> link = do_QueryInterface(aContent);
if (link) {
link->SetLinkState(eLinkState_Unknown);
}
document->ContentStatesChanged(aContent, nsnull, NS_EVENT_STATE_VISITED);
}
};
void
nsDocument::NotifyURIVisitednessChanged(nsIURI* aURI)
{
if (!mVisible) {
mVisitednessChangedURIs.AppendObject(aURI);
return;
}
nsUint32ToContentHashEntry* entry = mLinkMap.GetEntry(GetURIHash(aURI));
if (!entry)
return;
URIVisitNotifier visitor;
visitor.document = this;
aURI->GetSpec(visitor.matchURISpec);
entry->VisitContent(&visitor);
}
void
nsDocument::UpdateLinkMap()
{
NS_ASSERTION(mVisible,
"Should only be updating the link map in visible documents");
if (!mVisible)
return;
PRInt32 count = mVisitednessChangedURIs.Count();
for (PRInt32 i = 0; i < count; ++i) {
NotifyURIVisitednessChanged(mVisitednessChangedURIs[i]);
}
mVisitednessChangedURIs.Clear();
}

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

@ -44,6 +44,7 @@
#include "nsWeakReference.h"
#include "nsWeakPtr.h"
#include "nsVoidArray.h"
#include "nsHashSets.h"
#include "nsIDOMXMLDocument.h"
#include "nsIDOM3Document.h"
#include "nsIDOMDocumentView.h"
@ -110,6 +111,85 @@ class nsIFormControl;
struct nsRadioGroupStruct;
class nsOnloadBlocker;
/**
* Hashentry using a PRUint32 key and a cheap set of nsIContent* owning
* pointers for the value.
*
* @see nsTHashtable::EntryType for specification
*/
class nsUint32ToContentHashEntry : public PLDHashEntryHdr
{
public:
typedef const PRUint32& KeyType;
typedef const PRUint32* KeyTypePointer;
nsUint32ToContentHashEntry(const KeyTypePointer key) :
mValue(*key), mValOrHash(nsnull) { }
nsUint32ToContentHashEntry(const nsUint32ToContentHashEntry& toCopy) :
mValue(toCopy.mValue), mValOrHash(toCopy.mValOrHash)
{
// Pathetic attempt to not die: clear out the other mValOrHash so we're
// effectively stealing it. If toCopy is destroyed right after this,
// we'll be OK.
NS_CONST_CAST(nsUint32ToContentHashEntry&, toCopy).mValOrHash = nsnull;
NS_ERROR("Copying not supported. Fasten your seat belt.");
}
~nsUint32ToContentHashEntry() { Destroy(); }
KeyType GetKey() const { return mValue; }
KeyTypePointer GetKeyPointer() const { return &mValue; }
PRBool KeyEquals(KeyTypePointer aKey) const { return mValue == *aKey; }
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
static PLDHashNumber HashKey(KeyTypePointer aKey) { return *aKey; }
enum { ALLOW_MEMMOVE = PR_TRUE };
// Content set methods
nsresult PutContent(nsIContent* aContent);
void RemoveContent(nsIContent* aContent);
struct Visitor {
virtual void Visit(nsIContent* aContent) = 0;
};
void VisitContent(Visitor* aVisitor);
PRBool IsEmpty() { return mValOrHash == nsnull; }
private:
typedef unsigned long PtrBits;
typedef nsTHashtable<nsISupportsHashKey> HashSet;
/** Get the hash pointer (or null if we're not a hash) */
HashSet* GetHashSet()
{
return (PtrBits(mValOrHash) & 0x1) ? nsnull : (HashSet*)mValOrHash;
}
/** Find out whether it is an nsIContent (returns weak) */
nsIContent* GetContent()
{
return (PtrBits(mValOrHash) & 0x1)
? (nsIContent*)(PtrBits(mValOrHash) & ~0x1)
: nsnull;
}
/** Set the single element, adding a reference */
nsresult SetContent(nsIContent* aVal)
{
NS_IF_ADDREF(aVal);
mValOrHash = (void*)(PtrBits(aVal) | 0x1);
return NS_OK;
}
/** Initialize the hash */
nsresult InitHashSet(HashSet** aSet);
void Destroy();
private:
const PRUint32 mValue;
/** A hash or nsIContent ptr, depending on the lower bit (0=hash, 1=ptr) */
void* mValOrHash;
};
class nsDocHeaderData
{
@ -562,6 +642,10 @@ public:
virtual NS_HIDDEN_(void) BlockOnload();
virtual NS_HIDDEN_(void) UnblockOnload();
virtual NS_HIDDEN_(void) AddStyleRelevantLink(nsIContent* aContent, nsIURI* aURI);
virtual NS_HIDDEN_(void) ForgetLink(nsIContent* aContent);
virtual NS_HIDDEN_(void) NotifyURIVisitednessChanged(nsIURI* aURI);
protected:
void DispatchContentLoadedEvents();
@ -572,6 +656,8 @@ protected:
PRInt32& aCharsetSource,
nsACString& aCharset);
void UpdateLinkMap();
nsresult doCreateShell(nsPresContext* aContext,
nsIViewManager* aViewManager, nsStyleSet* aStyleSet,
nsCompatibility aCompatMode,
@ -592,7 +678,7 @@ protected:
return kNameSpaceID_None;
};
nsDocument() : nsIDocument() {}
nsDocument() : nsIDocument(), mVisible(PR_TRUE) {}
virtual ~nsDocument();
nsCString mReferrer;
@ -635,10 +721,11 @@ protected:
nsHashtable mRadioGroups;
// True if the document has been detached from its content viewer.
PRPackedBool mIsGoingAway;
PRPackedBool mIsGoingAway:1;
// True if the document is being destroyed.
PRPackedBool mInDestructor;
PRPackedBool mInDestructor:1;
// True if the document "page" is not hidden
PRPackedBool mVisible:1;
PRUint8 mXMLDeclarationBits;
@ -673,8 +760,12 @@ private:
PRUint32 mOnloadBlockCount;
nsCOMPtr<nsIRequest> mOnloadBlocker;
// A map from unvisited URI hashes to content elements
nsTHashtable<nsUint32ToContentHashEntry> mLinkMap;
// URIs whose visitedness has changed while we were hidden
nsCOMArray<nsIURI> mVisitednessChangedURIs;
};
#endif /* nsDocument_h___ */

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

@ -1897,6 +1897,10 @@ nsGenericElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
// anonymous content that the document is changing.
document->BindingManager()->ChangeDocumentFor(this, document, nsnull);
if (HasAttr(kNameSpaceID_XLink, nsHTMLAtoms::href)) {
document->ForgetLink(this);
}
nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(this);
if (domElement) {
@ -3488,6 +3492,20 @@ nsGenericElement::SetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
"Don't call SetAttr with unknown namespace");
if (kNameSpaceID_XLink == aNamespaceID && nsHTMLAtoms::href == aName) {
// XLink URI(s) might be changing. Drop the link from the map. If it
// is still style relevant it will be re-added by
// nsStyleUtil::IsSimpleXlink. Make sure to keep the style system
// consistent so this remains true! In particular if the style system
// were to get smarter and not restyling an XLink element if the href
// doesn't change in a "significant" way, we'd need to do the same
// significance check here.
nsIDocument* doc = GetCurrentDoc();
if (doc) {
doc->ForgetLink(this);
}
}
PRBool modification = PR_FALSE;
nsAutoString oldValue;
@ -3642,6 +3660,11 @@ nsGenericElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
nsIDocument *document = GetCurrentDoc();
mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
if (document) {
if (kNameSpaceID_XLink == aNameSpaceID && nsHTMLAtoms::href == aName) {
// XLink URI might be changing.
document->ForgetLink(this);
}
if (aNotify) {
document->AttributeWillChange(this, aNameSpaceID, aName);
}

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

@ -151,5 +151,6 @@ public:
// CSS 3 UI
#define NS_EVENT_STATE_REQUIRED 0x00000040
#define NS_EVENT_STATE_OPTIONAL 0x00000080
#define NS_EVENT_STATE_VISITED 0x00000100
#endif // nsIEventStateManager_h__

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

@ -195,8 +195,12 @@ nsHTMLAnchorElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
{
if (IsInDoc()) {
RegUnRegAccessKey(PR_FALSE);
GetCurrentDoc()->ForgetLink(this);
// If this link is ever reinserted into a document, it might
// be under a different xml:base, so forget the cached state now
mLinkState = eLinkState_Unknown;
}
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
}
@ -577,6 +581,13 @@ nsHTMLAnchorElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
nsAutoString val;
GetHref(val);
if (!val.Equals(aValue)) {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
doc->ForgetLink(this);
// The change to 'href' will cause style reresolution which will
// eventually recompute the link state and re-add this element
// to the link map if necessary.
}
SetLinkState(eLinkState_Unknown);
}
}

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

@ -228,6 +228,10 @@ nsHTMLAreaElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
{
if (IsInDoc()) {
RegUnRegAccessKey(PR_FALSE);
GetCurrentDoc()->ForgetLink(this);
// If this link is ever reinserted into a document, it might
// be under a different xml:base, so forget the cached state now
mLinkState = eLinkState_Unknown;
}
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
@ -243,6 +247,13 @@ nsHTMLAreaElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
}
if (aName == nsHTMLAtoms::href && aNameSpaceID == kNameSpaceID_None) {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
doc->ForgetLink(this);
// The change to 'href' will cause style reresolution which will
// eventually recompute the link state and re-add this element
// to the link map if necessary.
}
SetLinkState(eLinkState_Unknown);
}

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

@ -233,6 +233,12 @@ void
nsHTMLLinkElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
{
nsCOMPtr<nsIDocument> oldDoc = GetCurrentDoc();
if (oldDoc) {
GetCurrentDoc()->ForgetLink(this);
// If this link is ever reinserted into a document, it might
// be under a different xml:base, so forget the cached state now
mLinkState = eLinkState_Unknown;
}
// XXXbz we really shouldn't fire the event until after we've finished with
// the outermost UnbindFromTree... In particular, this can effectively cause
@ -289,6 +295,13 @@ nsHTMLLinkElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
PRBool aNotify)
{
if (aName == nsHTMLAtoms::href && kNameSpaceID_None == aNameSpaceID) {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
doc->ForgetLink(this);
// The change to 'href' will cause style reresolution which will
// eventually recompute the link state and re-add this element
// to the link map if necessary.
}
SetLinkState(eLinkState_Unknown);
}

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

@ -66,6 +66,7 @@
#include "nsIWebBrowserChrome.h"
#include "nsPoint.h"
#include "nsGfxCIID.h"
#include "nsIObserverService.h"
#include "nsIPrompt.h"
#include "nsIAuthPrompt.h"
#include "nsTextFormatter.h"
@ -7831,13 +7832,27 @@ NS_IMETHODIMP nsDocShell::MakeEditable(PRBool inWaitForUriLoad)
nsresult
nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect, nsIURI * aReferrer)
{
if (mItemType != typeContent)
if (mItemType != typeContent || !mGlobalHistory)
return NS_OK;
if (!mGlobalHistory)
return NS_OK;
PRBool visited;
nsresult rv = mGlobalHistory->IsVisited(aURI, &visited);
if (NS_FAILED(rv))
return rv;
rv = mGlobalHistory->AddURI(aURI, aRedirect, !IsFrame(), aReferrer);
if (NS_FAILED(rv))
return rv;
return mGlobalHistory->AddURI(aURI, aRedirect, !IsFrame(), aReferrer);
if (!visited) {
nsCOMPtr<nsIObserverService> obsService =
do_GetService("@mozilla.org/observer-service;1");
if (obsService) {
obsService->NotifyObservers(aURI, NS_LINK_VISITED_EVENT_TOPIC, nsnull);
}
}
return NS_OK;
}
//*****************************************************************************

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

@ -1846,14 +1846,16 @@ PresShell::Init(nsIDocument* aDocument,
PR_TRUE);
}
#ifdef MOZ_XUL
{
nsCOMPtr<nsIObserverService> os =
do_GetService("@mozilla.org/observer-service;1", &result);
if (os)
if (os) {
os->AddObserver(this, NS_LINK_VISITED_EVENT_TOPIC, PR_FALSE);
#ifdef MOZ_XUL
os->AddObserver(this, "chrome-flush-skin-caches", PR_FALSE);
}
#endif
}
}
// cache the drag service so we can check it during reflows
mDragService = do_GetService("@mozilla.org/widget/dragservice;1");
@ -7218,6 +7220,14 @@ PresShell::Observe(nsISupports* aSubject,
}
#endif
if (!nsCRT::strcmp(aTopic, NS_LINK_VISITED_EVENT_TOPIC)) {
nsCOMPtr<nsIURI> uri = do_QueryInterface(aSubject);
if (uri && mDocument) {
mDocument->NotifyURIVisitednessChanged(uri);
}
return NS_OK;
}
NS_WARNING("unrecognized topic in PresShell::Observe");
return NS_ERROR_FAILURE;
}

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

@ -3108,10 +3108,12 @@ static PRBool SelectorMatches(RuleProcessorData &data,
result = localTrue;
}
else if (nsCSSPseudoClasses::link == pseudoClass->mAtom) {
result = localTrue == (eLinkState_Unvisited == data.mLinkState);
result = (aStateMask & NS_EVENT_STATE_VISITED) ||
localTrue == (eLinkState_Unvisited == data.mLinkState);
}
else if (nsCSSPseudoClasses::visited == pseudoClass->mAtom) {
result = localTrue == (eLinkState_Visited == data.mLinkState);
result = (aStateMask & NS_EVENT_STATE_VISITED) ||
localTrue == (eLinkState_Visited == data.mLinkState);
}
}
else {
@ -3644,6 +3646,8 @@ PRBool IsStateSelector(nsCSSSelector& aSelector)
(pseudoClass->mAtom == nsCSSPseudoClasses::focus) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::hover) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::target) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::link) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::visited) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::required) ||
(pseudoClass->mAtom == nsCSSPseudoClasses::optional)) {
return PR_TRUE;

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

@ -480,13 +480,15 @@ NS_IMETHODIMP
nsHTMLStyleSheet::HasStateDependentStyle(StateRuleProcessorData* aData,
nsReStyleHint* aResult)
{
if (mActiveRule &&
(aData->mStateMask & NS_EVENT_STATE_ACTIVE) &&
aData->mStyledContent &&
if (aData->mStyledContent &&
aData->mIsHTMLContent &&
aData->mIsHTMLLink &&
aData->mContentTag == nsHTMLAtoms::a)
aData->mContentTag == nsHTMLAtoms::a &&
((mActiveRule && (aData->mStateMask & NS_EVENT_STATE_ACTIVE)) ||
(mLinkRule && (aData->mStateMask & NS_EVENT_STATE_VISITED)) ||
(mVisitedRule && (aData->mStateMask & NS_EVENT_STATE_VISITED)))) {
*aResult = eReStyle_Self;
}
else
*aResult = nsReStyleHint(0);

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

@ -458,6 +458,12 @@ PRBool nsStyleUtil::IsHTMLLink(nsIContent *aContent, nsIAtom *aTag, nsPresContex
} else {
linkState = eLinkState_NotLink;
}
if (linkState != eLinkState_NotLink) {
nsIDocument* doc = aPresContext->GetDocument();
if (doc) {
doc->AddStyleRelevantLink(aContent, hrefURI);
}
}
link->SetLinkState(linkState);
}
if (linkState != eLinkState_NotLink) {
@ -495,6 +501,10 @@ PRBool nsStyleUtil::IsSimpleXlink(nsIContent *aContent, nsPresContext *aPresCont
// no link handler? then all links are unvisited
*aState = eLinkState_Unvisited;
}
nsIDocument* doc = aPresContext->GetDocument();
if (doc) {
doc->AddStyleRelevantLink(aContent, absURI);
}
rv = PR_TRUE;
}

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

@ -485,6 +485,8 @@ nsGlobalHistory::nsGlobalHistory()
mIgnoreSchemes.AppendString(NS_LITERAL_STRING("ftp://"));
mIgnoreHostnames.AppendString(NS_LITERAL_STRING("www."));
mIgnoreHostnames.AppendString(NS_LITERAL_STRING("ftp."));
mTypedHiddenURIs.Init(3);
}
nsGlobalHistory::~nsGlobalHistory()
@ -695,8 +697,10 @@ nsGlobalHistory::AddExistingPageToDatabase(nsIMdbRow *row,
// if the page was typed, unhide it now because it's
// known to be valid
if (HasCell(mEnv, row, kToken_TypedColumn))
if (HasCell(mEnv, row, kToken_TypedColumn)) {
mTypedHiddenURIs.Remove(URISpec);
row->CutColumn(mEnv, kToken_HiddenColumn);
}
// Update last visit date.
// First get the old date so we can update observers...
@ -1333,6 +1337,16 @@ nsGlobalHistory::IsVisited(nsIURI* aURI, PRBool *_retval)
rv = FindRow(kToken_URLColumn, URISpec.get(), nsnull);
*_retval = NS_SUCCEEDED(rv);
// Hidden, typed URIs haven't really been visited yet. They've only
// been typed in and the actual load hasn't happened yet. We maintain
// the list of hidden+typed URIs in memory in mTypedHiddenURIs because
// the list will usually be small and checking the actual Mork row
// would require several dynamic memory allocations.
if (*_retval && mTypedHiddenURIs.Contains(URISpec))
{
*_retval = PR_FALSE;
}
return NS_OK;
}
@ -1443,6 +1457,7 @@ nsGlobalHistory::MarkPageAsTyped(nsIURI *aURI)
// We don't know if this is a valid URI yet. Hide it until it finishes
// loading.
SetRowValue(row, kToken_HiddenColumn, 1);
mTypedHiddenURIs.Put(spec);
}
return SetRowValue(row, kToken_TypedColumn, 1);

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

@ -60,6 +60,7 @@
#include "nsIAutoCompleteSearch.h"
#include "nsIAutoCompleteResult.h"
#include "nsIAutoCompleteResultTypes.h"
#include "nsHashSets.h"
//----------------------------------------------------------------------
//
@ -299,6 +300,9 @@ protected:
// meta-data tokens
mdb_column kToken_LastPageVisited;
// A set of the page URI specs that have been typed but not yet loaded
nsCStringHashSet mTypedHiddenURIs;
//
// AddPage-oriented stuff
//

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

@ -42,7 +42,8 @@
* Determine whether or not a given focused DOMWindow is in the content
* area.
**/
// linkNode is not used anymore
function openNewTabWith(href, linkNode, event, securityCheck, postData, sendReferrer)
{
if (securityCheck)
@ -77,11 +78,9 @@ function openNewTabWith(href, linkNode, event, securityCheck, postData, sendRefe
var referrer = (sendReferrer == false) ? null : getReferrer(document);
browser.loadOneTab(href, referrer, originCharset, postData, loadInBackground);
if (linkNode)
markLinkVisited(href, linkNode);
}
// linkNode is not used anymore
function openNewWindowWith(href, linkNode, securityCheck, postData, sendReferrer)
{
if (securityCheck)
@ -99,31 +98,6 @@ function openNewWindowWith(href, linkNode, securityCheck, postData, sendReferrer
var referrer = (sendReferrer == false) ? null : getReferrer(document);
window.openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no", href, charsetArg, referrer, postData);
if (linkNode)
markLinkVisited(href, linkNode);
}
function markLinkVisited(href, linkNode)
{
var globalHistory = Components.classes["@mozilla.org/browser/global-history;2"]
.getService(Components.interfaces.nsIGlobalHistory2);
var uri = makeURI(href);
if (!globalHistory.isVisited(uri)) {
globalHistory.addURI(uri, false, true, null);
var oldHref = linkNode.getAttribute("href");
if (typeof oldHref == "string") {
// Use setAttribute instead of direct assignment.
// (bug 217195, bug 187195)
linkNode.setAttribute("href", "");
linkNode.setAttribute("href", oldHref);
}
else {
// Converting to string implicitly would be a
// minor security hole (similar to bug 202994).
}
}
}
function urlSecurityCheck(url, doc)

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

@ -372,6 +372,8 @@ nsMdbTableEnumerator::Init(nsIMdbEnv* aEnv,
mdb_err err;
err = mTable->GetTableRowCursor(mEnv, -1, &mCursor);
if (err != 0) return NS_ERROR_FAILURE;
mTypedHiddenURIs.Init(3);
return NS_OK;
}
@ -706,8 +708,14 @@ nsGlobalHistory::AddExistingPageToDatabase(nsIMdbRow *row,
// if the page was typed, unhide it now because it's
// known to be valid
if (HasCell(mEnv, row, kToken_TypedColumn))
if (HasCell(mEnv, row, kToken_TypedColumn)) {
nsCAutoString URISpec;
rv = GetRowValue(row, kToken_URLColumn, URISpec);
NS_ENSURE_SUCCESS(rv, rv);
mTypedHiddenURIs.Remove(URISpec);
row->CutColumn(mEnv, kToken_HiddenColumn);
}
// Update last visit date.
// First get the old date so we can update observers...
@ -1252,7 +1260,17 @@ nsGlobalHistory::IsVisited(nsIURI* aURI, PRBool *_retval)
rv = FindRow(kToken_URLColumn, URISpec.get(), nsnull);
*_retval = NS_SUCCEEDED(rv);
// Hidden, typed URIs haven't really been visited yet. They've only
// been typed in and the actual load hasn't happened yet. We maintain
// the list of hidden+typed URIs in memory in mTypedHiddenURIs because
// the list will usually be small and checking the actual Mork row
// would require several dynamic memory allocations.
if (*_retval && mTypedHiddenURIs.Contains(URISpec))
{
*_retval = PR_FALSE;
}
return NS_OK;
}
@ -1357,6 +1375,7 @@ nsGlobalHistory::MarkPageAsTyped(nsIURI *aURI)
// We don't know if this is a valid URI yet. Hide it until it finishes
// loading.
SetRowValue(row, kToken_HiddenColumn, 1);
mTypedHiddenURIs.Put(spec);
}
return SetRowValue(row, kToken_TypedColumn, 1);

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

@ -57,6 +57,7 @@
#include "nsString.h"
#include "nsITimer.h"
#include "nsIAutoCompleteSession.h"
#include "nsHashSets.h"
//----------------------------------------------------------------------
//
@ -299,6 +300,8 @@ protected:
// meta-data tokens
mdb_column kToken_LastPageVisited;
mdb_column kToken_ByteOrder;
nsCStringHashSet mTypedHiddenURIs;
//
// AddPage-oriented stuff