зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
65891781d9
Коммит
c3ea09b7b0
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче