From 02dedb34c2b36255505c67a27e5b7dd3998b6335 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 4 Nov 2008 18:10:43 +0100 Subject: [PATCH 01/10] Fix for bug 457022 (Cache DOM wrappers in the DOM object). r/sr=jst. --- content/base/public/nsIContent.h | 13 -- content/base/public/nsIDocument.h | 8 +- content/base/public/nsINode.h | 18 +- content/base/src/nsContentList.cpp | 1 + content/base/src/nsContentList.h | 4 +- content/base/src/nsContentUtils.cpp | 9 - content/base/src/nsDOMAttribute.cpp | 1 + content/base/src/nsDocument.cpp | 55 +----- content/base/src/nsDocument.h | 4 - content/base/src/nsGenericDOMDataNode.cpp | 4 +- content/base/src/nsGenericElement.cpp | 5 +- content/base/src/nsGenericElement.h | 24 ++- content/base/src/nsNodeUtils.cpp | 15 +- content/base/src/nsXMLHttpRequest.cpp | 20 +- content/base/src/nsXMLHttpRequest.h | 34 +++- content/xbl/src/nsXBLProtoImpl.cpp | 8 +- dom/public/Makefile.in | 1 + dom/public/nsWrapperCache.h | 137 +++++++++++++ dom/src/base/nsDOMClassInfo.cpp | 224 ++++++++++++++++------ dom/src/base/nsDOMClassInfo.h | 23 ++- dom/src/base/nsGlobalWindow.cpp | 13 -- dom/src/base/nsJSEnvironment.cpp | 3 +- js/src/xpconnect/src/xpcconvert.cpp | 22 ++- js/src/xpconnect/src/xpcwrappednative.cpp | 55 ++++-- 24 files changed, 477 insertions(+), 224 deletions(-) create mode 100644 dom/public/nsWrapperCache.h diff --git a/content/base/public/nsIContent.h b/content/base/public/nsIContent.h index bc6772c89b13..d9d2e3c10c47 100644 --- a/content/base/public/nsIContent.h +++ b/content/base/public/nsIContent.h @@ -902,15 +902,6 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIContent, NS_ICONTENT_IID) nsContentUtils::TraverseListenerManager(tmp, cb); \ } -#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER \ - { \ - nsISupports *preservedWrapper = nsnull; \ - if (tmp->GetOwnerDoc()) \ - preservedWrapper = tmp->GetOwnerDoc()->GetReference(tmp); \ - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[preserved wrapper]");\ - cb.NoteXPCOMChild(preservedWrapper); \ - } - #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_USERDATA \ if (tmp->HasProperties()) { \ nsNodeUtils::TraverseUserData(tmp, cb); \ @@ -922,10 +913,6 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIContent, NS_ICONTENT_IID) tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER); \ } -#define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ - if (tmp->GetOwnerDoc()) \ - tmp->GetOwnerDoc()->RemoveReference(tmp); - #define NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA \ if (tmp->HasProperties()) { \ nsNodeUtils::UnlinkUserData(tmp); \ diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index c78ac3fd62fd..ac55a30e722a 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -96,8 +96,8 @@ class nsFrameLoader; // IID for the nsIDocument interface #define NS_IDOCUMENT_IID \ -{ 0x6304ae8e, 0x2634, 0x45ed, \ - { 0x9e, 0x09, 0x83, 0x09, 0x5b, 0x46, 0x72, 0x8b } } +{ 0x92b19d1c, 0x8f37, 0x4d4b, \ + { 0xa3, 0x42, 0xb5, 0xc6, 0x8b, 0x54, 0xde, 0x6c } } // Flag for AddStyleSheet(). #define NS_STYLESHEET_FROM_CATALOG (1 << 0) @@ -677,10 +677,6 @@ public: virtual void ResetToURI(nsIURI *aURI, nsILoadGroup* aLoadGroup, nsIPrincipal* aPrincipal) = 0; - virtual void AddReference(void *aKey, nsISupports *aReference) = 0; - virtual nsISupports *GetReference(void *aKey) = 0; - virtual void RemoveReference(void *aKey) = 0; - /** * Set the container (docshell) for this document. */ diff --git a/content/base/public/nsINode.h b/content/base/public/nsINode.h index 9b91448ee0d8..9d6f8cf87047 100644 --- a/content/base/public/nsINode.h +++ b/content/base/public/nsINode.h @@ -44,6 +44,7 @@ #include "nsTObserverArray.h" #include "nsINodeInfo.h" #include "nsCOMPtr.h" +#include "nsWrapperCache.h" class nsIContent; class nsIDocument; @@ -150,16 +151,17 @@ inline nsINode* NODE_FROM(C& aContent, D& aDocument) // IID for the nsINode interface #define NS_INODE_IID \ -{ 0x2593b0d5, 0x9a06, 0x4d6b, \ - { 0x9a, 0x10, 0xb1, 0x39, 0x9f, 0x1b, 0xa0, 0x8e } } - +{ 0x0f7b2557, 0xa09d, 0x468f, \ + { 0x93, 0x92, 0xf1, 0xf1, 0xd1, 0xfa, 0x31, 0x78 } } /** * An internal interface that abstracts some DOMNode-related parts that both * nsIContent and nsIDocument share. An instance of this interface has a list * of nsIContent children and provides access to them. */ -class nsINode : public nsPIDOMEventTarget { +class nsINode : public nsPIDOMEventTarget, + public nsWrapperCache +{ public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID) @@ -850,4 +852,12 @@ extern const nsIID kThisPtrOffsetsSID; NS_DEFINE_STATIC_IID_ACCESSOR(nsINode, NS_INODE_IID) + +#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER \ + tmp->TraverseWrapper(cb); + +#define NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER \ + tmp->ClearWrapper(); + + #endif /* nsINode_h___ */ diff --git a/content/base/src/nsContentList.cpp b/content/base/src/nsContentList.cpp index 0567dc19e53a..6ad28c084689 100644 --- a/content/base/src/nsContentList.cpp +++ b/content/base/src/nsContentList.cpp @@ -358,6 +358,7 @@ nsContentList::~nsContentList() // QueryInterface implementation for nsContentList NS_INTERFACE_TABLE_HEAD(nsContentList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_NODELIST_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsContentList) NS_CONTENT_LIST_INTERFACES(nsContentList) NS_INTERFACE_TABLE_ENTRY(nsContentList, nsIHTMLCollection) diff --git a/content/base/src/nsContentList.h b/content/base/src/nsContentList.h index 1f65c6de29bc..4a60b3fdf9bd 100644 --- a/content/base/src/nsContentList.h +++ b/content/base/src/nsContentList.h @@ -54,6 +54,7 @@ #include "nsIAtom.h" #include "nsINameSpaceManager.h" #include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" // Magic namespace id that means "match all namespaces". This is // negative so it won't collide with actual namespace constants. @@ -182,7 +183,8 @@ protected: class nsContentList : public nsBaseContentList, protected nsContentListKey, public nsIHTMLCollection, - public nsStubMutationObserver + public nsStubMutationObserver, + public nsWrapperCache { public: NS_DECL_ISUPPORTS_INHERITED diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 1a127741e61c..514849a23027 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -1119,15 +1119,6 @@ nsContentUtils::doReparentContentWrapper(nsIContent *aNode, getter_AddRefs(old_wrapper)); NS_ENSURE_SUCCESS(rv, rv); - if (aOldDocument) { - nsCOMPtr old_ref = aOldDocument->GetReference(aNode); - if (old_ref) { - // Transfer the reference from aOldDocument to aNewDocument - aOldDocument->RemoveReference(aNode); - aNewDocument->AddReference(aNode, old_ref); - } - } - // Whether or not aChild is already wrapped we must iterate through // its descendants since there's no guarantee that a descendant isn't // wrapped even if this child is not wrapped. That used to be true diff --git a/content/base/src/nsDOMAttribute.cpp b/content/base/src/nsDOMAttribute.cpp index e292811f7317..c809d3fb53db 100644 --- a/content/base/src/nsDOMAttribute.cpp +++ b/content/base/src/nsDOMAttribute.cpp @@ -101,6 +101,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttribute) NS_IMPL_CYCLE_COLLECTION_UNLINK_END // QueryInterface implementation for nsDOMAttribute NS_INTERFACE_TABLE_HEAD(nsDOMAttribute) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_NODE_INTERFACE_TABLE7(nsDOMAttribute, nsIDOMAttr, nsIAttribute, nsINode, nsIDOMNode, nsIDOM3Node, nsIDOM3Attr, nsPIDOMEventTarget) diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index f0245899e65e..11b8504744fe 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -1567,13 +1567,12 @@ nsDocument::~nsDocument() mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nsnull); delete mBoxObjectTable; } - - delete mContentWrapperHash; } NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument) NS_INTERFACE_TABLE_HEAD(nsDocument) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_DOCUMENT_INTERFACE_TABLE_BEGIN(nsDocument) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument) @@ -1753,9 +1752,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mCatalogSheets) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mVisitednessChangedURIs) - // Traverse any associated preserved wrapper. - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[preserved wrapper]"); - cb.NoteXPCOMChild(tmp->GetReference(tmp)); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER if (tmp->mSubDocuments && tmp->mSubDocuments->ops) { PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb); @@ -1786,12 +1783,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK_USERDATA - // Drop the content hash. - delete tmp->mContentWrapperHash; - tmp->mContentWrapperHash = nsnull; - tmp->mParentDocument = nsnull; + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + // nsDocument has a pretty complex destructor, so we're going to // assume that *most* cycles you actually want to break somewhere // else, and not unlink an awful lot here. @@ -6228,41 +6223,6 @@ nsDocument::FlushPendingNotifications(mozFlushType aType) } } -void -nsDocument::AddReference(void *aKey, nsISupports *aReference) -{ - if (mScriptGlobalObject) { - if (!mContentWrapperHash) { - mContentWrapperHash = new nsInterfaceHashtable; - if (mContentWrapperHash) { - mContentWrapperHash->Init(10); - } - } - - if (mContentWrapperHash) - mContentWrapperHash->Put(aKey, aReference); - } -} - -nsISupports* -nsDocument::GetReference(void *aKey) -{ - // NB: This method is part of content cycle collection, - // and must *not* Addref its return value. - - if (mContentWrapperHash) - return mContentWrapperHash->GetWeak(aKey); - return nsnull; -} - -void -nsDocument::RemoveReference(void *aKey) -{ - if (mContentWrapperHash) { - mContentWrapperHash->Remove(aKey); - } -} - nsIScriptEventManager* nsDocument::GetScriptEventManager() { @@ -6873,13 +6833,6 @@ nsDocument::Destroy() // leak-fixing if we fix DocumentViewerImpl to do cycle-collection, but // tearing down all those frame trees right now is the right thing to do. mExternalResourceMap.Shutdown(); - - // XXX We really should let cycle collection do this, but that currently still - // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684). - // When we start relying on cycle collection again we should remove the - // check for mScriptGlobalObject in AddReference. - delete mContentWrapperHash; - mContentWrapperHash = nsnull; } void diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index e83914bbb20e..1cdf14b563f6 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -750,9 +750,6 @@ public: nsIStyleRule* aStyleRule); virtual void FlushPendingNotifications(mozFlushType aType); - virtual void AddReference(void *aKey, nsISupports *aReference); - virtual nsISupports *GetReference(void *aKey); - virtual void RemoveReference(void *aKey); virtual nsIScriptEventManager* GetScriptEventManager(); virtual void SetXMLDeclaration(const PRUnichar *aVersion, const PRUnichar *aEncoding, @@ -1158,7 +1155,6 @@ protected: PRUint8 mIdMissCount; nsInterfaceHashtable *mBoxObjectTable; - nsInterfaceHashtable *mContentWrapperHash; // The channel that got passed to StartDocumentLoad(), if any nsCOMPtr mChannel; diff --git a/content/base/src/nsGenericDOMDataNode.cpp b/content/base/src/nsGenericDOMDataNode.cpp index 70e8eb00e8f6..e6856fddc593 100644 --- a/content/base/src/nsGenericDOMDataNode.cpp +++ b/content/base/src/nsGenericDOMDataNode.cpp @@ -105,7 +105,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGenericDOMDataNode) +NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsGenericDOMDataNode) NS_INTERFACE_MAP_ENTRY(nsIContent) NS_INTERFACE_MAP_ENTRY(nsINode) NS_INTERFACE_MAP_ENTRY(nsPIDOMEventTarget) diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 061ca3ce60b1..4010ccfdf84c 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -462,6 +462,7 @@ NS_IMPL_ADDREF(nsChildContentList) NS_IMPL_RELEASE(nsChildContentList) NS_INTERFACE_TABLE_HEAD(nsChildContentList) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_NODELIST_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsChildContentList) NS_INTERFACE_TABLE_ENTRY(nsChildContentList, nsINodeList) NS_INTERFACE_TABLE_ENTRY(nsChildContentList, nsIDOMNodeList) @@ -4066,7 +4067,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGenericElement) +NS_INTERFACE_MAP_BEGIN(nsGenericElement) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsGenericElement) NS_INTERFACE_MAP_ENTRY(nsIContent) NS_INTERFACE_MAP_ENTRY(nsINode) NS_INTERFACE_MAP_ENTRY(nsPIDOMEventTarget) diff --git a/content/base/src/nsGenericElement.h b/content/base/src/nsGenericElement.h index c3b69c6f5977..fc02e6e3a520 100644 --- a/content/base/src/nsGenericElement.h +++ b/content/base/src/nsGenericElement.h @@ -89,7 +89,8 @@ typedef unsigned long PtrBits; * and Item to its existing child list. * @see nsIDOMNodeList */ -class nsChildContentList : public nsINodeList +class nsChildContentList : public nsINodeList, + public nsWrapperCache { public: nsChildContentList(nsINode* aNode) @@ -110,6 +111,27 @@ public: mNode = nsnull; } + nsISupports* GetParentObject() + { + return mNode; + } + + static nsChildContentList* FromSupports(nsISupports* aSupports) + { + nsINodeList* list = static_cast(aSupports); +#ifdef DEBUG + { + nsCOMPtr list_qi = do_QueryInterface(aSupports); + + // If this assertion fires the QI implementation for the object in + // question doesn't use the nsINodeList pointer as the nsISupports + // pointer. That must be fixed, or we'll crash... + NS_ASSERTION(list_qi == list, "Uh, fix QI!"); + } +#endif + return static_cast(list); + } + private: // The node whose children make up the list (weak reference) nsINode* mNode; diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index 0542147aa768..6325a3ac5cbd 100755 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -564,26 +564,15 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, PRBool aClone, PRBool aDeep, } } else if (nodeInfoManager) { - nsCOMPtr oldRef; nsIDocument* oldDoc = aNode->GetOwnerDoc(); - if (oldDoc) { - if (aNode->IsNodeOfType(nsINode::eELEMENT)) { - oldDoc->ClearBoxObjectFor(static_cast(aNode)); - } - oldRef = oldDoc->GetReference(aNode); - if (oldRef) { - oldDoc->RemoveReference(aNode); - } + if (oldDoc && aNode->IsNodeOfType(nsINode::eELEMENT)) { + oldDoc->ClearBoxObjectFor(static_cast(aNode)); } aNode->mNodeInfo.swap(newNodeInfo); nsIDocument* newDoc = aNode->GetOwnerDoc(); if (newDoc) { - if (oldRef) { - newDoc->AddReference(aNode, oldRef); - } - nsPIDOMWindow* window = newDoc->GetInnerWindow(); if (window) { nsCOMPtr elm; diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index b60f611c36d0..513ea902571a 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -530,13 +530,7 @@ GetDocumentFromScriptContext(nsIScriptContext *aScriptContext) NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREventTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXHREventTarget) - if (tmp->mOwner) { - nsCOMPtr doc = - do_QueryInterface(tmp->mOwner->GetExtantDocument()); - if (doc) { - cb.NoteXPCOMChild(doc->GetReference(tmp)); - } - } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLoadListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnAbortListener) @@ -548,13 +542,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXHREventTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXHREventTarget) - if (tmp->mOwner) { - nsCOMPtr doc = - do_QueryInterface(tmp->mOwner->GetExtantDocument()); - if (doc) { - doc->RemoveReference(tmp); - } - } + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLoadListener) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnAbortListener) @@ -565,7 +553,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXHREventTarget) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner) NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXHREventTarget) +NS_INTERFACE_MAP_BEGIN(nsXHREventTarget) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsXHREventTarget) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXMLHttpRequestEventTarget) NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestEventTarget) NS_INTERFACE_MAP_ENTRY(nsPIDOMEventTarget) diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h index 9291ba575386..417e35003c42 100644 --- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -73,6 +73,7 @@ #include "nsITimer.h" #include "nsIPrivateDOMEvent.h" #include "nsDOMProgressEvent.h" +#include "nsIScriptGlobalObject.h" class nsILoadGroup; @@ -159,7 +160,8 @@ protected: class nsXHREventTarget : public nsIXMLHttpRequestEventTarget, public nsPIDOMEventTarget, - public nsIDOMNSEventTarget + public nsIDOMNSEventTarget, + public nsWrapperCache { public: nsXHREventTarget() : mLang(nsIProgrammingLanguage::JAVASCRIPT) {} @@ -207,6 +209,36 @@ public: } return NS_OK; } + + void GetParentObject(nsIScriptGlobalObject **aParentObject) + { + if (mOwner) { + CallQueryInterface(mOwner, aParentObject); + } + else { + *aParentObject = nsnull; + } + } + + static nsXHREventTarget* FromSupports(nsISupports* aSupports) + { + nsIXMLHttpRequestEventTarget* target = + static_cast(aSupports); +#ifdef DEBUG + { + nsCOMPtr target_qi = + do_QueryInterface(aSupports); + + // If this assertion fires the QI implementation for the object in + // question doesn't use the nsIXMLHttpRequestEventTarget pointer as the + // nsISupports pointer. That must be fixed, or we'll crash... + NS_ASSERTION(target_qi == target, "Uh, fix QI!"); + } +#endif + + return static_cast(target); + } + protected: nsRefPtr mOnLoadListener; nsRefPtr mOnErrorListener; diff --git a/content/xbl/src/nsXBLProtoImpl.cpp b/content/xbl/src/nsXBLProtoImpl.cpp index 5c3e6fb70c03..27edd77b5673 100644 --- a/content/xbl/src/nsXBLProtoImpl.cpp +++ b/content/xbl/src/nsXBLProtoImpl.cpp @@ -151,13 +151,7 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding, if (NS_FAILED(rv)) return rv; - // Root ourselves in the document. - nsIDocument* doc = aBoundElement->GetOwnerDoc(); - if (doc) { - nsCOMPtr nativeWrapper(do_QueryInterface(wrapper)); - if (nativeWrapper) - doc->AddReference(aBoundElement, nativeWrapper); - } + aBoundElement->PreserveWrapper(); wrapper.swap(*aScriptObjectHolder); diff --git a/dom/public/Makefile.in b/dom/public/Makefile.in index e2e9f499ef76..dc1cb6eb2737 100644 --- a/dom/public/Makefile.in +++ b/dom/public/Makefile.in @@ -71,6 +71,7 @@ EXPORTS=nsIScriptContext.h \ nsDOMString.h \ nsDOMJSUtils.h \ nsDOMScriptObjectHolder.h \ + nsWrapperCache.h \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/dom/public/nsWrapperCache.h b/dom/public/nsWrapperCache.h new file mode 100644 index 000000000000..c077ee619759 --- /dev/null +++ b/dom/public/nsWrapperCache.h @@ -0,0 +1,137 @@ +/* ***** 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 Gecko DOM code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Peter Van der Beken + * + * 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 ***** */ + +#ifndef nsWrapperCache_h___ +#define nsWrapperCache_h___ + +#include "nsCycleCollectionParticipant.h" + +typedef unsigned long PtrBits; + +#define NS_WRAPPERCACHE_IID \ +{ 0x3a51ca81, 0xddab, 0x422c, \ + { 0x95, 0x3a, 0x13, 0x06, 0x28, 0x0e, 0xee, 0x14 } } + +/** + * Class to store the XPCWrappedNative for an object. This can only be used + * with objects that only have one XPCWrappedNative at a time (usually ensured + * by setting an explicit parent in the PreCreate hook for the class). This + * object can be gotten by calling QueryInterface, note that this breaks XPCOM + * rules a bit (this object doesn't derive from nsISupports). + */ +class nsWrapperCache +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID) + + nsWrapperCache() : mWrapperPtrBits(0) + { + } + ~nsWrapperCache() + { + if (PreservingWrapper()) { + GetWrapper()->Release(); + } + } + + /** + * This method returns an nsIXPConnectWrappedNative, but we want to avoid + * including nsIXPConnect, because we don't want to make everyone require + * JS and XPConnect. + */ + nsISupports* GetWrapper() + { + return reinterpret_cast(mWrapperPtrBits & ~kWrapperBitMask); + } + + /** + * This method takes an nsIXPConnectWrappedNative, but we want to avoid + * including nsIXPConnect, because we don't want to make everyone require + * JS and XPConnect. + */ + void SetWrapper(nsISupports* aWrapper) + { + NS_ASSERTION(!mWrapperPtrBits, "Already have a wrapper!"); + mWrapperPtrBits = reinterpret_cast(aWrapper); + } + + void ClearWrapper() + { + if (PreservingWrapper()) { + GetWrapper()->Release(); + } + mWrapperPtrBits = 0; + } + + void PreserveWrapper() + { + NS_ASSERTION(mWrapperPtrBits, "No wrapper to preserve?"); + if (!PreservingWrapper()) { + NS_ADDREF(reinterpret_cast(mWrapperPtrBits)); + mWrapperPtrBits |= WRAPPER_BIT_PRESERVED; + } + } + + void TraverseWrapper(nsCycleCollectionTraversalCallback &cb) + { + if (PreservingWrapper()) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mWrapper"); + cb.NoteXPCOMChild(GetWrapper()); + } + } + +private: + PRBool PreservingWrapper() + { + return mWrapperPtrBits & WRAPPER_BIT_PRESERVED; + } + + enum { WRAPPER_BIT_PRESERVED = 1 << 0 }; + enum { kWrapperBitMask = 0x1 }; + + PtrBits mWrapperPtrBits; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID) + +#define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY \ + if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) { \ + *aInstancePtr = static_cast(this); \ + return NS_OK; \ + } + +#endif /* nsWrapperCache_h___ */ diff --git a/dom/src/base/nsDOMClassInfo.cpp b/dom/src/base/nsDOMClassInfo.cpp index b41aa24c844b..85f435a6ab2c 100644 --- a/dom/src/base/nsDOMClassInfo.cpp +++ b/dom/src/base/nsDOMClassInfo.cpp @@ -171,6 +171,7 @@ // ContentList includes #include "nsContentList.h" +#include "nsGenericElement.h" // Event related includes #include "nsIEventListenerManager.h" @@ -212,7 +213,7 @@ #include "nsIDOMLSProgressEvent.h" #include "nsIDOMParser.h" #include "nsIDOMSerializer.h" -#include "nsIXMLHttpRequest.h" +#include "nsXMLHttpRequest.h" // includes needed for the prototype chain interfaces #include "nsIDOMNavigator.h" @@ -489,7 +490,8 @@ static const char kDOMStringBundleURL[] = ((DOM_DEFAULT_SCRIPTABLE_FLAGS | \ nsIXPCScriptable::WANT_GETPROPERTY | \ nsIXPCScriptable::WANT_ADDPROPERTY | \ - nsIXPCScriptable::WANT_SETPROPERTY) & \ + nsIXPCScriptable::WANT_SETPROPERTY | \ + nsIXPCScriptable::WANT_FINALIZE) & \ ~nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY) // We need to let JavaScript QI elements to interfaces that are not in @@ -513,15 +515,22 @@ static const char kDOMStringBundleURL[] = nsIXPCScriptable::WANT_ADDPROPERTY | \ nsIXPCScriptable::WANT_DELPROPERTY | \ nsIXPCScriptable::WANT_GETPROPERTY | \ - nsIXPCScriptable::WANT_ENUMERATE | \ - nsIXPCScriptable::WANT_POSTCREATE | \ - nsIXPCScriptable::WANT_FINALIZE) + nsIXPCScriptable::WANT_ENUMERATE) #define ARRAY_SCRIPTABLE_FLAGS \ (DOM_DEFAULT_SCRIPTABLE_FLAGS | \ nsIXPCScriptable::WANT_GETPROPERTY | \ nsIXPCScriptable::WANT_ENUMERATE) +#define NODELIST_SCRIPTABLE_FLAGS \ + (ARRAY_SCRIPTABLE_FLAGS | \ + nsIXPCScriptable::WANT_FINALIZE) + +#define EVENTTARGET_SCRIPTABLE_FLAGS \ + (DOM_DEFAULT_SCRIPTABLE_FLAGS | \ + nsIXPCScriptable::WANT_ADDPROPERTY | \ + nsIXPCScriptable::WANT_FINALIZE) + #define DOMCLASSINFO_STANDARD_FLAGS \ (nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::DOM_OBJECT) @@ -632,7 +641,7 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(ProcessingInstruction, nsNodeSH, NODE_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(Notation, nsNodeSH, NODE_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(NodeList, nsNodeListSH, ARRAY_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(NodeList, nsNodeListSH, NODELIST_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(NamedNodeMap, nsNamedNodeMapSH, ARRAY_SCRIPTABLE_FLAGS) @@ -883,9 +892,7 @@ static nsDOMClassInfoData sClassInfoData[] = { ARRAY_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA_WITH_NAME(ContentList, HTMLCollection, - nsContentListSH, - ARRAY_SCRIPTABLE_FLAGS | - nsIXPCScriptable::WANT_PRECREATE) + nsContentListSH, NODELIST_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(XMLStylesheetProcessingInstruction, nsNodeSH, NODE_SCRIPTABLE_FLAGS) @@ -1206,8 +1213,7 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(XMLHttpProgressEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(XMLHttpRequest, nsEventTargetSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS | - nsIXPCScriptable::WANT_ADDPROPERTY) + EVENTTARGET_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(ClientRect, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -1278,8 +1284,7 @@ static nsDOMClassInfoData sClassInfoData[] = { DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(XMLHttpRequestUpload, nsEventTargetSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS | - nsIXPCScriptable::WANT_ADDPROPERTY) + EVENTTARGET_SCRIPTABLE_FLAGS) // DOM Traversal NodeIterator class NS_DEFINE_CLASSINFO_DATA(NodeIterator, nsDOMGenericSH, @@ -4167,28 +4172,14 @@ nsDOMClassInfo::GetClassInfoInstance(nsDOMClassInfoData* aData) } // static -nsresult +void nsDOMClassInfo::PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper) { - nsISupports *native = aWrapper->Native(); - nsCOMPtr node(do_QueryInterface(native)); - - nsCOMPtr doc; - if (node) { - nsCOMPtr domdoc; - node->GetOwnerDocument(getter_AddRefs(domdoc)); - doc = do_QueryInterface(domdoc); + nsWrapperCache* cache; + CallQueryInterface(aWrapper->Native(), &cache); + if (cache) { + cache->PreserveWrapper(); } - - if (!doc) { - doc = do_QueryInterface(native); - } - - if (doc) { - nsCOMPtr n(do_QueryInterface(node)); - doc->AddReference(n, aWrapper); - } - return NS_OK; } @@ -6910,6 +6901,16 @@ nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj, return rv; } +NS_IMETHODIMP +nsNodeSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj) +{ + nsINode* node = static_cast(wrapper->Native()); + node->SetWrapper(wrapper); + + return nsEventReceiverSH::PostCreate(wrapper, cx, obj); +} + NS_IMETHODIMP nsNodeSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsval id, jsval *vp, PRBool *_retval) @@ -6928,6 +6929,13 @@ nsNodeSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, return DefineVoidProp(cx, obj, id, objp); } + if (id == sOnload_id || id == sOnerror_id) { + // Make sure that this node can't go away while waiting for a + // network load that could fire an event handler. + nsINode* node = static_cast(wrapper->Native()); + node->PreserveWrapper(); + } + return nsEventReceiverSH::NewResolve(wrapper, cx, obj, id, flags, objp, _retval); } @@ -6996,6 +7004,16 @@ nsNodeSH::GetFlags(PRUint32 *aFlags) return NS_OK; } +NS_IMETHODIMP +nsNodeSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj) +{ + nsINode* node = static_cast(wrapper->Native()); + node->ClearWrapper(); + + return NS_OK; +} + // EventReceiver helper // static @@ -7246,12 +7264,6 @@ nsEventReceiverSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsval id, PRUint32 flags, JSObject **objp, PRBool *_retval) { - if (id == sOnload_id || id == sOnerror_id) { - // Make sure that this node can't go away while waiting for a - // network load that could fire an event handler. - nsDOMClassInfo::PreserveNodeWrapper(wrapper); - } - if (!JSVAL_IS_STRING(id)) { return NS_OK; } @@ -7336,6 +7348,30 @@ nsEventReceiverSH::AddProperty(nsIXPConnectWrappedNative *wrapper, // EventTarget helper +NS_IMETHODIMP +nsEventTargetSH::PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj) +{ + nsXHREventTarget *target = nsXHREventTarget::FromSupports(nativeObj); + + nsCOMPtr native_parent; + target->GetParentObject(getter_AddRefs(native_parent)); + + *parentObj = native_parent ? native_parent->GetGlobalJSObject() : globalObj; + + return NS_OK; +} + +NS_IMETHODIMP +nsEventTargetSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj) +{ + nsXHREventTarget *target = nsXHREventTarget::FromSupports(wrapper->Native()); + target->SetWrapper(wrapper); + + return nsDOMGenericSH::PostCreate(wrapper, cx, obj); +} + NS_IMETHODIMP nsEventTargetSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsval id, @@ -7358,26 +7394,24 @@ nsEventTargetSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, if (id == sAddEventListener_id) { return NS_OK; } - nsISupports* native = wrapper->Native(); - nsCOMPtr target = do_QueryInterface(native); - if (target) { - nsCOMPtr scriptContext; - target->GetContextForEventHandlers(getter_AddRefs(scriptContext)); - if (scriptContext) { - nsCOMPtr window = - do_QueryInterface(scriptContext->GetGlobalObject()); - if (window) { - nsCOMPtr doc = - do_QueryInterface(window->GetExtantDocument()); - if (doc) { - doc->AddReference(native, wrapper); - } - } - } - } + + nsXHREventTarget *target = nsXHREventTarget::FromSupports(wrapper->Native()); + target->PreserveWrapper(); + return NS_OK; } +NS_IMETHODIMP +nsEventTargetSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj) +{ + nsXHREventTarget *target = nsXHREventTarget::FromSupports(wrapper->Native()); + target->ClearWrapper(); + + return NS_OK; +} + + // Element helper NS_IMETHODIMP @@ -7650,6 +7684,49 @@ nsArraySH::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, // NodeList scriptable helper +nsresult +nsNodeListSH::PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj) +{ + nsWrapperCache* cache; + CallQueryInterface(nativeObj, &cache); + if (!cache) { + *parentObj = globalObj; + return NS_OK; + } + + // nsChildContentList is the only class that uses nsNodeListSH and has a + // cached wrapper. + nsChildContentList *list = nsChildContentList::FromSupports(nativeObj); + nsISupports *native_parent = list->GetParentObject(); + if (!native_parent) { + return NS_ERROR_FAILURE; + } + + jsval v; + nsCOMPtr holder; + nsresult rv = WrapNative(cx, globalObj, native_parent, + NS_GET_IID(nsISupports), &v, + getter_AddRefs(holder)); + + *parentObj = JSVAL_TO_OBJECT(v); + + return rv; +} + +NS_IMETHODIMP +nsNodeListSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj) +{ + nsWrapperCache* cache; + CallQueryInterface(wrapper->Native(), &cache); + if (cache) { + cache->SetWrapper(wrapper); + } + + return nsArraySH::PostCreate(wrapper, cx, obj); +} + nsresult nsNodeListSH::GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, PRUint32 *length) @@ -7688,6 +7765,19 @@ nsNodeListSH::GetItemAt(nsISupports *aNative, PRUint32 aIndex, return list->GetNodeAt(aIndex); } +NS_IMETHODIMP +nsNodeListSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj) +{ + nsWrapperCache* cache; + CallQueryInterface(wrapper->Native(), &cache); + if (cache) { + cache->ClearWrapper(); + } + + return NS_OK; +} + // StringList scriptable helper @@ -7825,8 +7915,7 @@ nsContentListSH::PreCreate(nsISupports *nativeObj, JSContext *cx, nsISupports *native_parent = contentList->GetParentObject(); if (!native_parent) { - *parentObj = globalObj; - return NS_OK; + return NS_ERROR_FAILURE; } jsval v; @@ -7840,6 +7929,16 @@ nsContentListSH::PreCreate(nsISupports *nativeObj, JSContext *cx, return rv; } +NS_IMETHODIMP +nsContentListSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj) +{ + nsContentList *list = nsContentList::FromSupports(wrapper->Native()); + list->SetWrapper(wrapper); + + return nsNamedArraySH::PostCreate(wrapper, cx, obj); +} + nsresult nsContentListSH::GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, PRUint32 *length) @@ -7867,6 +7966,15 @@ nsContentListSH::GetNamedItem(nsISupports *aNative, const nsAString& aName, return list->GetNamedItem(aName, aResult); } +NS_IMETHODIMP +nsContentListSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj) +{ + nsContentList *list = nsContentList::FromSupports(wrapper->Native()); + list->ClearWrapper(); + + return NS_OK; +} // Document helper for document.location and document.on* @@ -8091,7 +8199,7 @@ nsDocumentSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, doc->SetJSObject(nsnull); - return NS_OK; + return nsNodeSH::Finalize(wrapper, cx, obj); } // HTMLDocument helper diff --git a/dom/src/base/nsDOMClassInfo.h b/dom/src/base/nsDOMClassInfo.h index f03297212905..70740623b15c 100644 --- a/dom/src/base/nsDOMClassInfo.h +++ b/dom/src/base/nsDOMClassInfo.h @@ -172,7 +172,7 @@ public: ::JS_GET_CLASS(cx, obj) == sXPCNativeWrapperClass; } - static nsresult PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper); + static void PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper); protected: friend nsIClassInfo* NS_GetDOMClassInfoInstance(nsDOMClassInfoID aID); @@ -400,11 +400,17 @@ protected: { } public: + NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj); + NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj); NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsval id, PRUint32 flags, JSObject **objp, PRBool *_retval); NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsval id, jsval *vp, PRBool *_retval); + NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj); static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) { @@ -556,6 +562,8 @@ protected: public: NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj, JSObject **parentObj); + NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj); NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsval id, jsval *vp, PRBool *_retval); NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, @@ -566,6 +574,8 @@ public: NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsval id, jsval *vp, PRBool *_retval); NS_IMETHOD GetFlags(PRUint32 *aFlags); + NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj); static nsIClassInfo *doCreate(nsDOMClassInfoData* aData) { @@ -668,6 +678,13 @@ protected: } public: + NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj); + NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj); + NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj); + virtual nsresult GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, PRUint32 *length); virtual nsISupports* GetItemAt(nsISupports *aNative, PRUint32 aIndex, @@ -779,6 +796,10 @@ protected: public: NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj, JSObject **parentObj); + NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj); + NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx, + JSObject *obj); virtual nsresult GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, PRUint32 *length); diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index 8d16510e59af..d5ad0785e5c0 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -1001,13 +1001,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocumentPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDoc) - // Traverse any associated preserved wrappers. - { - if (tmp->mDoc) { - cb.NoteXPCOMChild(tmp->mDoc->GetReference(tmp)); - } - } - // Traverse stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChromeEventHandler) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument) @@ -1040,12 +1033,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mApplicationCache) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocumentPrincipal) - // Unlink any associated preserved wrapper. - if (tmp->mDoc) { - tmp->mDoc->RemoveReference(tmp); - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDoc) - } - // Unlink stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChromeEventHandler) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument) diff --git a/dom/src/base/nsJSEnvironment.cpp b/dom/src/base/nsJSEnvironment.cpp index 6408eccc8376..61c9c000f580 100644 --- a/dom/src/base/nsJSEnvironment.cpp +++ b/dom/src/base/nsJSEnvironment.cpp @@ -3372,7 +3372,8 @@ nsJSContext::ScriptExecuted() NS_IMETHODIMP nsJSContext::PreserveWrapper(nsIXPConnectWrappedNative *aWrapper) { - return nsDOMClassInfo::PreserveNodeWrapper(aWrapper); + nsDOMClassInfo::PreserveNodeWrapper(aWrapper); + return NS_OK; } //static diff --git a/js/src/xpconnect/src/xpcconvert.cpp b/js/src/xpconnect/src/xpcconvert.cpp index c6c87a9e85c8..1fd1f820ff2e 100644 --- a/js/src/xpconnect/src/xpcconvert.cpp +++ b/js/src/xpconnect/src/xpcconvert.cpp @@ -48,6 +48,7 @@ #include "nsIAtom.h" #include "XPCWrapper.h" #include "nsJSPrincipals.h" +#include "nsWrapperCache.h" //#define STRICT_CHECK_OF_UNICODE #ifdef STRICT_CHECK_OF_UNICODE @@ -1091,15 +1092,28 @@ XPCConvert::NativeInterface2JSObject(XPCCallContext& ccx, if(!iface) return JS_FALSE; + nsresult rv; XPCWrappedNative* wrapper; - nsresult rv = XPCWrappedNative::GetNewOrUsed(ccx, src, xpcscope, - iface, isGlobal, - &wrapper); + nsWrapperCache* cache; + src->QueryInterface(NS_GET_IID(nsWrapperCache), (void**)&cache); + if(cache && + (wrapper = static_cast(cache->GetWrapper()))) + { + NS_ADDREF(wrapper); + wrapper->FindTearOff(ccx, iface, JS_FALSE, &rv); + } + else + { + rv = XPCWrappedNative::GetNewOrUsed(ccx, src, xpcscope, iface, + isGlobal, &wrapper); + } + if(pErr) *pErr = rv; if(NS_SUCCEEDED(rv) && wrapper) { uint32 flags = 0; + JSObject *flat = wrapper->GetFlatJSObject(); if (allowNativeWrapper && wrapper->GetScope() != xpcscope) { // Cross scope access detected. Check if chrome code @@ -1157,7 +1171,6 @@ XPCConvert::NativeInterface2JSObject(XPCCallContext& ccx, flags = script ? JS_GetScriptFilenameFlags(script) : 0; NS_ASSERTION(flags != JSFILENAME_NULL, "null script filename"); - JSObject *flat = wrapper->GetFlatJSObject(); if(!JS_IsSystemObject(ccx, flat)) { @@ -1265,7 +1278,6 @@ XPCConvert::NativeInterface2JSObject(XPCCallContext& ccx, } } - JSObject *flat = wrapper->GetFlatJSObject(); const char *name = STOBJ_GET_CLASS(flat)->name; if(allowNativeWrapper && !(flags & JSFILENAME_SYSTEM) && diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 1804a2a3ba90..a14f64ffd6bf 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -45,6 +45,7 @@ #include "nsCRT.h" #include "XPCNativeWrapper.h" #include "XPCWrapper.h" +#include "nsWrapperCache.h" /***************************************************************************/ @@ -589,27 +590,13 @@ XPCWrappedNative::GetUsedOnly(XPCCallContext& ccx, XPCWrappedNative** resultWrapper) { NS_ASSERTION(Object, "XPCWrappedNative::GetUsedOnly was called with a null Object"); - nsCOMPtr identity; -#ifdef XPC_IDISPATCH_SUPPORT - // XXX See GetNewOrUsed for more info on this - if(Interface->GetIID()->Equals(NSID_IDISPATCH)) - identity = Object; - else -#endif - identity = do_QueryInterface(Object); - - if(!identity) - { - NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!"); - return NS_ERROR_FAILURE; - } XPCWrappedNative* wrapper; - Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap(); - - { // scoped lock - XPCAutoLock lock(Scope->GetRuntime()->GetMapLock()); - wrapper = map->Find(identity); + nsWrapperCache* cache; + Object->QueryInterface(NS_GET_IID(nsWrapperCache), (void**)&cache); + if(cache) + { + wrapper = static_cast(cache->GetWrapper()); if(!wrapper) { *resultWrapper = nsnull; @@ -617,6 +604,36 @@ XPCWrappedNative::GetUsedOnly(XPCCallContext& ccx, } NS_ADDREF(wrapper); } + else + { + nsCOMPtr identity; +#ifdef XPC_IDISPATCH_SUPPORT + // XXX See GetNewOrUsed for more info on this + if(Interface->GetIID()->Equals(NSID_IDISPATCH)) + identity = Object; + else +#endif + identity = do_QueryInterface(Object); + + if(!identity) + { + NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!"); + return NS_ERROR_FAILURE; + } + + Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap(); + + { // scoped lock + XPCAutoLock lock(Scope->GetRuntime()->GetMapLock()); + wrapper = map->Find(identity); + if(!wrapper) + { + *resultWrapper = nsnull; + return NS_OK; + } + NS_ADDREF(wrapper); + } + } nsresult rv; if(!wrapper->FindTearOff(ccx, Interface, JS_FALSE, &rv)) From 1a9f80641035cc4ac3273541f8cffa83b600cae2 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 4 Nov 2008 18:15:37 +0100 Subject: [PATCH 02/10] Fix for bug 462947 ("ASSERTION: already initialized" - nsMimeTypeArray::GetMimeTypes). r/sr=bz --- dom/src/base/nsMimeTypeArray.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dom/src/base/nsMimeTypeArray.cpp b/dom/src/base/nsMimeTypeArray.cpp index 0c6aab0f899a..48e60f510ab8 100644 --- a/dom/src/base/nsMimeTypeArray.cpp +++ b/dom/src/base/nsMimeTypeArray.cpp @@ -209,6 +209,7 @@ nsMimeTypeArray::NamedItem(const nsAString& aName, nsIDOMMimeType** aReturn) void nsMimeTypeArray::Clear() { + mInited = PR_FALSE; mMimeTypeArray.Clear(); mPluginMimeTypeCount = 0; } @@ -229,7 +230,7 @@ nsresult nsMimeTypeArray::GetMimeTypes() if (rv == NS_OK) { // count up all possible MimeTypes, and collect them here. Later, // we'll remove duplicates. - mPluginMimeTypeCount = 0; + PRUint32 pluginMimeTypeCount = 0; PRUint32 pluginCount = 0; rv = pluginArray->GetLength(&pluginCount); if (rv == NS_OK) { @@ -240,12 +241,16 @@ nsresult nsMimeTypeArray::GetMimeTypes() plugin) { PRUint32 mimeTypeCount = 0; if (plugin->GetLength(&mimeTypeCount) == NS_OK) - mPluginMimeTypeCount += mimeTypeCount; + pluginMimeTypeCount += mimeTypeCount; } } // now we know how many there are, start gathering them. - if (!mMimeTypeArray.SetCapacity(mPluginMimeTypeCount)) + if (!mMimeTypeArray.SetCapacity(pluginMimeTypeCount)) return NS_ERROR_OUT_OF_MEMORY; + + mPluginMimeTypeCount = pluginMimeTypeCount; + mInited = PR_TRUE; + PRUint32 k; for (k = 0; k < pluginCount; k++) { nsIDOMPlugin* plugin = nsnull; From bb151f0e41c73160bc0b34f53fe8425179f18720 Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 4 Nov 2008 18:18:12 +0100 Subject: [PATCH 03/10] Fix for bug 462926 (Crash [@ xpc_qsSelfRef::~xpc_qsSelfRef] with getUserData.call). r/sr=jst --- js/src/xpconnect/crashtests/crashtests.list | 1 + js/src/xpconnect/src/xpcquickstubs.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/xpconnect/crashtests/crashtests.list b/js/src/xpconnect/crashtests/crashtests.list index 54947a55de1d..ce10de174a2e 100644 --- a/js/src/xpconnect/crashtests/crashtests.list +++ b/js/src/xpconnect/crashtests/crashtests.list @@ -8,3 +8,4 @@ load 394810-1.html load 403356-1.html load 418139-1.svg load 420513-1.html +load 462926.html diff --git a/js/src/xpconnect/src/xpcquickstubs.h b/js/src/xpconnect/src/xpcquickstubs.h index 0bafed8f5f08..99379b36dd79 100644 --- a/js/src/xpconnect/src/xpcquickstubs.h +++ b/js/src/xpconnect/src/xpcquickstubs.h @@ -266,7 +266,7 @@ public: struct xpc_qsSelfRef { - xpc_qsSelfRef() {} + xpc_qsSelfRef() : ptr(nsnull) {} explicit xpc_qsSelfRef(nsISupports *p) : ptr(p) {} ~xpc_qsSelfRef() { NS_IF_RELEASE(ptr); } From 1b2252f6c60c2c4b4afd0c198c7c0a7aab22a8cf Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Tue, 4 Nov 2008 15:38:16 +0100 Subject: [PATCH 04/10] Bug 462793 - Changing listbox selection with keyboard no longer scrolls to make the newly selected item visible, r+sr=roc --- content/base/src/nsGenericElement.cpp | 4 ++-- content/html/document/src/nsImageDocument.cpp | 2 +- docshell/base/nsDocShell.cpp | 5 ++--- dom/src/base/nsGlobalWindow.cpp | 2 +- layout/base/nsDocumentViewer.cpp | 4 ++-- layout/base/nsPresShell.cpp | 5 ++--- layout/forms/nsListControlFrame.cpp | 4 ++-- layout/forms/nsTextControlFrame.cpp | 2 +- layout/generic/nsIScrollableFrame.h | 4 ++-- layout/generic/nsSelection.cpp | 5 ++--- widget/src/cocoa/nsChildView.mm | 2 +- 11 files changed, 18 insertions(+), 21 deletions(-) diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 4010ccfdf84c..c3dc73f151be 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -1103,7 +1103,7 @@ nsNSElementTearoff::SetScrollTop(PRInt32 aScrollTop) if (NS_SUCCEEDED(rv)) { rv = view->ScrollTo(xPos, nsPresContext::CSSPixelsToAppUnits(aScrollTop), - NS_VMREFRESH_IMMEDIATE); + 0); } } @@ -1145,7 +1145,7 @@ nsNSElementTearoff::SetScrollLeft(PRInt32 aScrollLeft) if (NS_SUCCEEDED(rv)) { rv = view->ScrollTo(nsPresContext::CSSPixelsToAppUnits(aScrollLeft), - yPos, NS_VMREFRESH_IMMEDIATE); + yPos, 0); } } diff --git a/content/html/document/src/nsImageDocument.cpp b/content/html/document/src/nsImageDocument.cpp index f7ccb7f2263f..60bde0f37543 100644 --- a/content/html/document/src/nsImageDocument.cpp +++ b/content/html/document/src/nsImageDocument.cpp @@ -490,7 +490,7 @@ nsImageDocument::ScrollImageTo(PRInt32 aX, PRInt32 aY, PRBool restoreImage) nsRect portRect = view->View()->GetBounds(); view->ScrollTo(nsPresContext::CSSPixelsToAppUnits(aX/ratio) - portRect.width/2, nsPresContext::CSSPixelsToAppUnits(aY/ratio) - portRect.height/2, - NS_VMREFRESH_IMMEDIATE); + 0); return NS_OK; } diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index c52a6d868383..157e718ca265 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -4183,7 +4183,7 @@ nsDocShell::SetCurScrollPos(PRInt32 scrollOrientation, PRInt32 curPos) y = 0; // fix compiler warning, not actually executed } - NS_ENSURE_SUCCESS(scrollView->ScrollTo(x, y, NS_VMREFRESH_IMMEDIATE), + NS_ENSURE_SUCCESS(scrollView->ScrollTo(x, y, 0), NS_ERROR_FAILURE); return NS_OK; } @@ -4198,8 +4198,7 @@ nsDocShell::SetCurScrollPosEx(PRInt32 curHorizontalPos, PRInt32 curVerticalPos) return NS_ERROR_FAILURE; } - NS_ENSURE_SUCCESS(scrollView->ScrollTo(curHorizontalPos, curVerticalPos, - NS_VMREFRESH_IMMEDIATE), + NS_ENSURE_SUCCESS(scrollView->ScrollTo(curHorizontalPos, curVerticalPos, 0), NS_ERROR_FAILURE); return NS_OK; } diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index d5ad0785e5c0..803f962c214f 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -4584,7 +4584,7 @@ nsGlobalWindow::ScrollTo(PRInt32 aXScroll, PRInt32 aYScroll) result = view->ScrollTo(nsPresContext::CSSPixelsToAppUnits(aXScroll), nsPresContext::CSSPixelsToAppUnits(aYScroll), - NS_VMREFRESH_IMMEDIATE); + 0); } return result; diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 246c56633c37..4c2360569a1e 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -3663,7 +3663,7 @@ DocumentViewerImpl::PrintPreviewNavigate(PRInt16 aType, PRInt32 aPageNum) // Check to see if we can short circut scrolling to the top if (aType == nsIWebBrowserPrint::PRINTPREVIEW_HOME || (aType == nsIWebBrowserPrint::PRINTPREVIEW_GOTO_PAGENUM && aPageNum == 1)) { - scrollableView->ScrollTo(0, 0, PR_TRUE); + scrollableView->ScrollTo(0, 0, 0); return NS_OK; } @@ -3750,7 +3750,7 @@ DocumentViewerImpl::PrintPreviewNavigate(PRInt16 aType, PRInt32 aPageNum) nscoord newYPosn = nscoord(mPrintEngine->GetPrintPreviewScale() * float(fndPageFrame->GetPosition().y - deadSpaceGap)); - scrollableView->ScrollTo(0, newYPosn, PR_TRUE); + scrollableView->ScrollTo(0, newYPosn, 0); } return NS_OK; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 688beadbd7e4..92e2bf01e226 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -3734,7 +3734,7 @@ PresShell::GoToAnchor(const nsAString& aAnchorName, PRBool aScroll) mViewManager->GetRootScrollableView(&scrollingView); if (scrollingView) { // Scroll to the top of the page - scrollingView->ScrollTo(0, 0, NS_VMREFRESH_IMMEDIATE); + scrollingView->ScrollTo(0, 0, 0); } } } @@ -3961,8 +3961,7 @@ static void ScrollViewToShowRect(nsIScrollableView* aScrollingView, NSToCoordRound(frameAlignX - visibleRect.width * (aHPercent / 100.0f)); } - aScrollingView->ScrollTo(scrollOffsetX, scrollOffsetY, - NS_VMREFRESH_IMMEDIATE); + aScrollingView->ScrollTo(scrollOffsetX, scrollOffsetY, 0); } NS_IMETHODIMP diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index b63bd2594937..2d795406a24e 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -2324,7 +2324,7 @@ nsListControlFrame::ScrollToFrame(nsIContent* aOptElement) if (scrollableView) { // if null is passed in we scroll to 0,0 if (nsnull == aOptElement) { - scrollableView->ScrollTo(0, 0, PR_TRUE); + scrollableView->ScrollTo(0, 0, 0); return NS_OK; } @@ -2382,7 +2382,7 @@ nsListControlFrame::ScrollToFrame(nsIContent* aOptElement) } else { y = fRect.y; } - scrollableView->ScrollTo(pnt.x, y, PR_TRUE); + scrollableView->ScrollTo(pnt.x, y, 0); } } diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index 45110a8ccc25..ce9c90271ed8 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -2787,7 +2787,7 @@ nsTextControlFrame::SetValue(const nsAString& aValue) // Scroll the upper left corner of the text control's // content area back into view. - scrollableView->ScrollTo(0, 0, NS_VMREFRESH_NO_SYNC); + scrollableView->ScrollTo(0, 0, 0); } } else diff --git a/layout/generic/nsIScrollableFrame.h b/layout/generic/nsIScrollableFrame.h index a78d1037be4c..83800841180e 100644 --- a/layout/generic/nsIScrollableFrame.h +++ b/layout/generic/nsIScrollableFrame.h @@ -99,10 +99,10 @@ public: * legal. Updates the display based on aUpdateFlags. * @param aX left edge to scroll to * @param aY top edge to scroll to - * @param aUpdateFlags passed onto nsIViewManager->UpdateView() + * @param aUpdateFlags indicate smooth or async scrolling * @return error status */ - virtual void ScrollTo(nsPoint aScrollPosition, PRUint32 aFlags = NS_VMREFRESH_NO_SYNC)=0; + virtual void ScrollTo(nsPoint aScrollPosition, PRUint32 aFlags = 0)=0; virtual nsIScrollableView* GetScrollableView() = 0; diff --git a/layout/generic/nsSelection.cpp b/layout/generic/nsSelection.cpp index 758166d96a0b..ba303ae34981 100644 --- a/layout/generic/nsSelection.cpp +++ b/layout/generic/nsSelection.cpp @@ -5089,8 +5089,7 @@ nsTypedSelection::ScrollPointIntoClipView(nsPresContext *aPresContext, nsIView * // Now scroll the view! - result = scrollableView->ScrollTo(bounds.x + dx, bounds.y + dy, - NS_VMREFRESH_NO_SYNC); + result = scrollableView->ScrollTo(bounds.x + dx, bounds.y + dy, 0); if (NS_FAILED(result)) return result; @@ -6400,7 +6399,7 @@ nsTypedSelection::ScrollRectIntoView(nsIScrollableView *aScrollableView, } } - aScrollableView->ScrollTo(scrollOffsetX, scrollOffsetY, NS_VMREFRESH_IMMEDIATE); + aScrollableView->ScrollTo(scrollOffsetX, scrollOffsetY, 0); if (aScrollParentViews) { diff --git a/widget/src/cocoa/nsChildView.mm b/widget/src/cocoa/nsChildView.mm index 7616856fd06d..f8b0d853b437 100644 --- a/widget/src/cocoa/nsChildView.mm +++ b/widget/src/cocoa/nsChildView.mm @@ -2668,7 +2668,7 @@ NSEvent* gLastDragEvent = nil; PRInt32 p2a = mGeckoChild->GetDeviceContext()->AppUnitsPerDevPixel(); nscoord newX = mHandScrollStartScrollX + NSIntPixelsToAppUnits(deltaX, p2a); nscoord newY = mHandScrollStartScrollY + NSIntPixelsToAppUnits(deltaY, p2a); - aScrollableView->ScrollTo(newX, newY, NS_VMREFRESH_IMMEDIATE); + aScrollableView->ScrollTo(newX, newY, 0); NS_OBJC_END_TRY_ABORT_BLOCK; } From 67f77c661c6a991c191ba473d3815f331251009d Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Tue, 4 Nov 2008 18:21:38 +0100 Subject: [PATCH 05/10] Fix for bug 462929 ("ASSERTION: We can't deal with objects that have the same classinfo but different offset tables" with MathML element and other element). r/sr=jst. --- .../mathml/content/src/nsMathMLElement.cpp | 37 ++++--------------- dom/public/nsDOMClassInfoID.h | 4 ++ dom/src/base/nsDOMClassInfo.cpp | 15 ++++++++ 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/content/mathml/content/src/nsMathMLElement.cpp b/content/mathml/content/src/nsMathMLElement.cpp index 31055bf5275a..34f36854fa8d 100644 --- a/content/mathml/content/src/nsMathMLElement.cpp +++ b/content/mathml/content/src/nsMathMLElement.cpp @@ -56,35 +56,14 @@ //---------------------------------------------------------------------- // nsISupports methods: -NS_IMETHODIMP -nsMathMLElement::QueryInterface(REFNSIID aIID, void** aInstancePtr) -{ - NS_PRECONDITION(aInstancePtr, "null out param"); - - nsresult rv = nsMathMLElementBase::QueryInterface(aIID, aInstancePtr); - - if (NS_SUCCEEDED(rv)) - return rv; - - nsISupports *inst = nsnull; - - if (aIID.Equals(NS_GET_IID(nsIDOMNode))) { - inst = static_cast(this); - } else if (aIID.Equals(NS_GET_IID(nsIDOMElement))) { - inst = static_cast(this); - } else if (aIID.Equals(NS_GET_IID(nsIClassInfo))) { - inst = NS_GetDOMClassInfoInstance(eDOMClassInfo_Element_id); - NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY); - } else { - return PostQueryInterface(aIID, aInstancePtr); - } - - NS_ADDREF(inst); - - *aInstancePtr = inst; - - return NS_OK; -} +NS_INTERFACE_TABLE_HEAD(nsMathMLElement) + NS_NODE_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsMathMLElement) + NS_INTERFACE_TABLE_ENTRY(nsMathMLElement, nsIDOMNode) + NS_INTERFACE_TABLE_ENTRY(nsMathMLElement, nsIDOMElement) + NS_OFFSET_AND_INTERFACE_TABLE_END + NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE + NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(MathMLElement) +NS_ELEMENT_INTERFACE_MAP_END NS_IMPL_ADDREF_INHERITED(nsMathMLElement, nsMathMLElementBase) NS_IMPL_RELEASE_INHERITED(nsMathMLElement, nsMathMLElementBase) diff --git a/dom/public/nsDOMClassInfoID.h b/dom/public/nsDOMClassInfoID.h index 89e37611352e..a5258e57d46e 100644 --- a/dom/public/nsDOMClassInfoID.h +++ b/dom/public/nsDOMClassInfoID.h @@ -454,6 +454,10 @@ enum nsDOMClassInfoID { eDOMClassInfo_SimpleGestureEvent_id, +#ifdef MOZ_MATHML + eDOMClassInfo_MathMLElement_id, +#endif + // This one better be the last one in this list eDOMClassInfoIDCount }; diff --git a/dom/src/base/nsDOMClassInfo.cpp b/dom/src/base/nsDOMClassInfo.cpp index 85f435a6ab2c..eac4da28c2fd 100644 --- a/dom/src/base/nsDOMClassInfo.cpp +++ b/dom/src/base/nsDOMClassInfo.cpp @@ -1299,6 +1299,11 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(SimpleGestureEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) + +#ifdef MOZ_MATHML + NS_DEFINE_CLASSINFO_DATA_WITH_NAME(MathMLElement, Element, nsElementSH, + ELEMENT_SCRIPTABLE_FLAGS) +#endif }; // Objects that shuld be constructable through |new Name();| @@ -3542,6 +3547,16 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END +#ifdef MOZ_MATHML + DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(MathMLElement, nsIDOMElement) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMElement) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSElement) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget) + DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Node) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMNodeSelector) + DOM_CLASSINFO_MAP_END +#endif + #ifdef NS_DEBUG { PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData); From d47cc410f698ccf1003be59ec7a369633f780cba Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Tue, 4 Nov 2008 18:05:36 +0000 Subject: [PATCH 06/10] Land the blocklist url change from bug 449027 and update the in-tree blocklist to the new schema. r=robstrong --- browser/app/blocklist.xml | 10 ++++++++++ browser/app/profile/firefox.js | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/browser/app/blocklist.xml b/browser/app/blocklist.xml index 23c628dd98ea..5c6daf7b3720 100644 --- a/browser/app/blocklist.xml +++ b/browser/app/blocklist.xml @@ -31,10 +31,20 @@ + + + + + + + + + + diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index b9093342d9dd..acba4fc38797 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -83,7 +83,7 @@ pref("extensions.blocklist.interval", 86400); // Controls what level the blocklist switches from warning about items to forcibly // blocking them. pref("extensions.blocklist.level", 2); -pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/2/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/"); +pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/"); pref("extensions.blocklist.detailsURL", "http://%LOCALE%.www.mozilla.com/%LOCALE%/blocklist/"); // Dictionary download preference From 4fc459d715d23a93faf0121b22e362ef3769f8d2 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Tue, 4 Nov 2008 11:05:24 -0500 Subject: [PATCH 07/10] Bug 459724. Fix normalization of flags in the DNS cache to allow other work that depends on these flags to happen. r+sr=bzbarsky --- netwerk/dns/src/nsHostResolver.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/netwerk/dns/src/nsHostResolver.cpp b/netwerk/dns/src/nsHostResolver.cpp index 6045a32eb61e..5b5629cd6e54 100644 --- a/netwerk/dns/src/nsHostResolver.cpp +++ b/netwerk/dns/src/nsHostResolver.cpp @@ -173,7 +173,7 @@ nsHostRecord::Create(const nsHostKey *key, nsHostRecord **result) } rec->host = ((char *) rec) + sizeof(nsHostRecord); - rec->flags = RES_KEY_FLAGS(key->flags); + rec->flags = key->flags; rec->af = key->af; rec->_refc = 1; // addref @@ -214,7 +214,7 @@ static PLDHashNumber HostDB_HashKey(PLDHashTable *table, const void *key) { const nsHostKey *hk = static_cast(key); - return PL_DHashStringKey(table, hk->host) ^ hk->flags ^ hk->af; + return PL_DHashStringKey(table, hk->host) ^ RES_KEY_FLAGS(hk->flags) ^ hk->af; } static PRBool @@ -226,7 +226,7 @@ HostDB_MatchEntry(PLDHashTable *table, const nsHostKey *hk = static_cast(key); return !strcmp(he->rec->host, hk->host) && - he->rec->flags == hk->flags && + RES_KEY_FLAGS (he->rec->flags) == RES_KEY_FLAGS(hk->flags) && he->rec->af == hk->af; } @@ -490,6 +490,7 @@ nsHostResolver::ResolveHost(const char *host, PR_APPEND_LINK(callback, &he->rec->callbacks); if (!he->rec->resolving) { + he->rec->flags = flags; rv = IssueLookup(he->rec); if (NS_FAILED(rv)) PR_REMOVE_AND_INIT_LINK(callback); From f33e25510d498d7450a34b85f329f99f3c3d7b8b Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Tue, 4 Nov 2008 11:05:46 -0500 Subject: [PATCH 08/10] Bug 459724. Make page reload bypass the DNS cache. r+sr=bzbarsky --- netwerk/base/public/nsISocketTransport.idl | 18 ++++++++++++++- netwerk/base/src/nsSocketTransport2.cpp | 22 ++++++++++++++++++- netwerk/base/src/nsSocketTransport2.h | 3 ++- netwerk/protocol/http/src/nsHttp.h | 4 ++++ netwerk/protocol/http/src/nsHttpChannel.cpp | 4 ++++ .../protocol/http/src/nsHttpConnection.cpp | 7 ++++-- netwerk/protocol/http/src/nsHttpConnection.h | 2 +- 7 files changed, 54 insertions(+), 6 deletions(-) diff --git a/netwerk/base/public/nsISocketTransport.idl b/netwerk/base/public/nsISocketTransport.idl index a9fd2a664779..5af0ddbae22b 100644 --- a/netwerk/base/public/nsISocketTransport.idl +++ b/netwerk/base/public/nsISocketTransport.idl @@ -51,7 +51,7 @@ native PRNetAddr(union PRNetAddr); * NOTE: This is a free-threaded interface, meaning that the methods on * this interface may be called from any thread. */ -[scriptable, uuid(66418cc8-5f5d-4f52-a7f9-db8fb3b2cfe6)] +[scriptable, uuid(ef3f4993-cfbc-4e5a-9509-16deafe16549)] interface nsISocketTransport : nsITransport { /** @@ -134,6 +134,22 @@ interface nsISocketTransport : nsITransport const unsigned long STATUS_SENDING_TO = 0x804b0005; const unsigned long STATUS_WAITING_FOR = 0x804b000a; const unsigned long STATUS_RECEIVING_FROM = 0x804b0006; + + /** + * connectionFlags is a bitmask that can be used to modify underlying + * behavior of the socket connection. + */ + attribute unsigned long connectionFlags; + + /** + * Values for the connectionFlags + * + * When making a new connection BYPASS_CACHE will force the Necko DNS + * cache entry to be refreshed with a new call to NSPR if it is set before + * opening the new stream. + */ + const unsigned long BYPASS_CACHE = (1 << 0); + }; %{C++ diff --git a/netwerk/base/src/nsSocketTransport2.cpp b/netwerk/base/src/nsSocketTransport2.cpp index eff1592ea514..a9f5d23bf88a 100644 --- a/netwerk/base/src/nsSocketTransport2.cpp +++ b/netwerk/base/src/nsSocketTransport2.cpp @@ -707,6 +707,7 @@ nsSocketTransport::nsSocketTransport() , mProxyPort(0) , mProxyTransparent(PR_FALSE) , mProxyTransparentResolvesHost(PR_FALSE) + , mConnectionFlags(0) , mState(STATE_CLOSED) , mAttached(PR_FALSE) , mInputClosed(PR_TRUE) @@ -946,7 +947,11 @@ nsSocketTransport::ResolveHost() mResolving = PR_TRUE; - rv = dns->AsyncResolve(SocketHost(), 0, this, nsnull, + PRUint32 dnsFlags = 0; + if (mConnectionFlags & nsSocketTransport::BYPASS_CACHE) + dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE; + + rv = dns->AsyncResolve(SocketHost(), dnsFlags, this, nsnull, getter_AddRefs(mDNSRequest)); if (NS_SUCCEEDED(rv)) { LOG((" advancing to STATE_RESOLVING\n")); @@ -1946,6 +1951,21 @@ nsSocketTransport::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) } +NS_IMETHODIMP +nsSocketTransport::GetConnectionFlags(PRUint32 *value) +{ + *value = mConnectionFlags; + return NS_OK; +} + +NS_IMETHODIMP +nsSocketTransport::SetConnectionFlags(PRUint32 value) +{ + mConnectionFlags = value; + return NS_OK; +} + + #ifdef ENABLE_SOCKET_TRACING #include diff --git a/netwerk/base/src/nsSocketTransport2.h b/netwerk/base/src/nsSocketTransport2.h index de4133ff467d..35efa229a46a 100644 --- a/netwerk/base/src/nsSocketTransport2.h +++ b/netwerk/base/src/nsSocketTransport2.h @@ -202,7 +202,8 @@ private: PRUint16 mProxyPort; PRPackedBool mProxyTransparent; PRPackedBool mProxyTransparentResolvesHost; - + PRUint32 mConnectionFlags; + PRUint16 SocketPort() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyPort : mPort; } const nsCString &SocketHost() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyHost : mHost; } diff --git a/netwerk/protocol/http/src/nsHttp.h b/netwerk/protocol/http/src/nsHttp.h index 21a2cbd471c4..ab167e75fa71 100644 --- a/netwerk/protocol/http/src/nsHttp.h +++ b/netwerk/protocol/http/src/nsHttp.h @@ -104,6 +104,10 @@ typedef PRUint8 nsHttpVersion; // preventing it from being reclaimed, even after the transaction completes. #define NS_HTTP_STICKY_CONNECTION (1<<2) +// a transaction with this caps flag will, upon opening a new connection, +// bypass the local DNS cache +#define NS_HTTP_REFRESH_DNS (1<<3) + //----------------------------------------------------------------------------- // some default values //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/src/nsHttpChannel.cpp b/netwerk/protocol/http/src/nsHttpChannel.cpp index cdd805be3c29..adbc595d03a7 100644 --- a/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -4010,6 +4010,10 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close")) mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE | NS_HTTP_ALLOW_PIPELINING); + if ((mLoadFlags & VALIDATE_ALWAYS) || + (BYPASS_LOCAL_CACHE(mLoadFlags))) + mCaps |= NS_HTTP_REFRESH_DNS; + mIsPending = PR_TRUE; mWasOpened = PR_TRUE; diff --git a/netwerk/protocol/http/src/nsHttpConnection.cpp b/netwerk/protocol/http/src/nsHttpConnection.cpp index 47f0e1cd9439..fbea4ff8cb24 100644 --- a/netwerk/protocol/http/src/nsHttpConnection.cpp +++ b/netwerk/protocol/http/src/nsHttpConnection.cpp @@ -140,7 +140,7 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps) // if we don't have a socket transport then create a new one if (!mSocketTransport) { - rv = CreateTransport(); + rv = CreateTransport(caps); if (NS_FAILED(rv)) goto loser; } @@ -424,7 +424,7 @@ nsHttpConnection::ResumeRecv() //----------------------------------------------------------------------------- nsresult -nsHttpConnection::CreateTransport() +nsHttpConnection::CreateTransport(PRUint8 caps) { nsresult rv; @@ -452,6 +452,9 @@ nsHttpConnection::CreateTransport() getter_AddRefs(strans)); if (NS_FAILED(rv)) return rv; + if (caps & NS_HTTP_REFRESH_DNS) + strans->SetConnectionFlags(nsISocketTransport::BYPASS_CACHE); + // NOTE: these create cyclical references, which we break inside // nsHttpConnection::Close rv = strans->SetEventSink(this, nsnull); diff --git a/netwerk/protocol/http/src/nsHttpConnection.h b/netwerk/protocol/http/src/nsHttpConnection.h index 98cd848c7762..7ca9d914754c 100644 --- a/netwerk/protocol/http/src/nsHttpConnection.h +++ b/netwerk/protocol/http/src/nsHttpConnection.h @@ -125,7 +125,7 @@ private: // called to cause the underlying socket to start speaking SSL nsresult ProxyStartSSL(); - nsresult CreateTransport(); + nsresult CreateTransport(PRUint8 caps); nsresult OnTransactionDone(nsresult reason); nsresult OnSocketWritable(); nsresult OnSocketReadable(); From dbf40a5ca6aee6a7709cfd3bb866178f2bd1a7a6 Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Tue, 4 Nov 2008 11:14:29 -0500 Subject: [PATCH 09/10] Bug 453403. Add DNS prefetching, similar to what Google chrome does. r+sr=bzbarsky --- content/base/src/nsContentSink.cpp | 23 ++ content/base/src/nsContentSink.h | 4 + content/base/src/nsDocument.cpp | 1 + content/base/src/nsGkAtomList.h | 1 + content/html/content/src/Makefile.in | 1 + .../html/content/src/nsHTMLAnchorElement.cpp | 18 ++ .../html/document/src/nsHTMLContentSink.cpp | 7 + content/xml/document/src/nsXMLContentSink.cpp | 12 + layout/build/nsLayoutStatics.cpp | 8 + netwerk/base/public/nsNetError.h | 8 + netwerk/base/src/Makefile.in | 1 + netwerk/build/nsNetModule.cpp | 6 +- netwerk/dns/public/nsIDNSService.idl | 9 +- netwerk/dns/src/nsDNSService2.cpp | 18 +- netwerk/dns/src/nsDNSService2.h | 1 + netwerk/dns/src/nsHostResolver.cpp | 240 ++++++++++++++---- netwerk/dns/src/nsHostResolver.h | 41 ++- netwerk/protocol/http/src/nsHttpChannel.cpp | 8 + 18 files changed, 348 insertions(+), 59 deletions(-) diff --git a/content/base/src/nsContentSink.cpp b/content/base/src/nsContentSink.cpp index 17ebda8cd175..b724af7295c8 100644 --- a/content/base/src/nsContentSink.cpp +++ b/content/base/src/nsContentSink.cpp @@ -101,6 +101,8 @@ #include "nsIDocumentLoader.h" #include "nsICachingChannel.h" #include "nsICacheEntryDescriptor.h" +#include "nsGenericHTMLElement.h" +#include "nsHTMLDNSPrefetch.h" PRLogModuleInfo* gContentSinkLogModuleInfo; @@ -721,6 +723,10 @@ nsContentSink::ProcessLink(nsIContent* aElement, PrefetchHref(aHref, aElement, hasPrefetch); } + if ((!aHref.IsEmpty()) && linkTypes.IndexOf(NS_LITERAL_STRING("dns-prefetch")) != -1) { + PrefetchDNS(aHref); + } + // is it a stylesheet link? if (linkTypes.IndexOf(NS_LITERAL_STRING("stylesheet")) == -1) { return NS_OK; @@ -852,6 +858,23 @@ nsContentSink::PrefetchHref(const nsAString &aHref, } } +void +nsContentSink::PrefetchDNS(const nsAString &aHref) +{ + nsAutoString hostname; + + if (StringBeginsWith(aHref, NS_LITERAL_STRING("//"))) { + hostname = Substring(aHref, 2); + } + else + nsGenericHTMLElement::GetHostnameFromHrefString(aHref, hostname); + + nsRefPtr prefetch = new nsHTMLDNSPrefetch(hostname, mDocument); + if (prefetch) { + prefetch->PrefetchLow(); + } +} + nsresult nsContentSink::GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey) { diff --git a/content/base/src/nsContentSink.h b/content/base/src/nsContentSink.h index f2c8c38bdbb5..5aa84af41eb2 100644 --- a/content/base/src/nsContentSink.h +++ b/content/base/src/nsContentSink.h @@ -192,6 +192,10 @@ protected: void PrefetchHref(const nsAString &aHref, nsIContent *aSource, PRBool aExplicit); + // aHref can either be the usual URI format or of the form "//www.hostname.com" + // without a scheme. + void PrefetchDNS(const nsAString &aHref); + // Gets the cache key (used to identify items in a cache) of the channel. nsresult GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey); diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 11b8504744fe..36cd79fd11cf 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -6521,6 +6521,7 @@ nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel) "content-language", "content-disposition", "refresh", + "x-dns-prefetch-control", // add more http headers if you need // XXXbz don't add content-location support without reading bug // 238654 and its dependencies/dups first. diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index d4a2e3bc12e1..54e0fa137619 100755 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -987,6 +987,7 @@ GK_ATOM(headerWindowTarget, "window-target") GK_ATOM(withParam, "with-param") GK_ATOM(wizard, "wizard") GK_ATOM(wrap, "wrap") +GK_ATOM(headerDNSPrefetchControl,"x-dns-prefetch-control") GK_ATOM(xml, "xml") GK_ATOM(xmlns, "xmlns") GK_ATOM(xmp, "xmp") diff --git a/content/html/content/src/Makefile.in b/content/html/content/src/Makefile.in index 962a240e57f2..f9562ccc0c1a 100644 --- a/content/html/content/src/Makefile.in +++ b/content/html/content/src/Makefile.in @@ -83,6 +83,7 @@ EXPORTS = \ CPPSRCS = \ nsClientRect.cpp \ + nsHTMLDNSPrefetch.cpp \ nsGenericHTMLElement.cpp \ nsFormSubmission.cpp \ nsImageMapUtils.cpp \ diff --git a/content/html/content/src/nsHTMLAnchorElement.cpp b/content/html/content/src/nsHTMLAnchorElement.cpp index e373bbc9a9c5..1887aaf79cf1 100644 --- a/content/html/content/src/nsHTMLAnchorElement.cpp +++ b/content/html/content/src/nsHTMLAnchorElement.cpp @@ -64,6 +64,8 @@ #include "nsIPresShell.h" #include "nsIDocument.h" +#include "nsHTMLDNSPrefetch.h" + nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult); class nsHTMLAnchorElement : public nsGenericHTMLElement, @@ -135,11 +137,26 @@ public: protected: // The cached visited state nsLinkState mLinkState; + + void PrefetchDNS(); }; NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor) +void +nsHTMLAnchorElement::PrefetchDNS() +{ + nsCOMPtr hrefURI; + GetHrefURI(getter_AddRefs(hrefURI)); + + if (hrefURI) { + nsRefPtr prefetch = + new nsHTMLDNSPrefetch(hrefURI, GetOwnerDoc()); + if (prefetch) + prefetch->PrefetchLow(); + } +} nsHTMLAnchorElement::nsHTMLAnchorElement(nsINodeInfo *aNodeInfo) : nsGenericHTMLElement(aNodeInfo), @@ -212,6 +229,7 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, RegUnRegAccessKey(PR_TRUE); } + PrefetchDNS(); return rv; } diff --git a/content/html/document/src/nsHTMLContentSink.cpp b/content/html/document/src/nsHTMLContentSink.cpp index 9c9be73c2a54..0ba96e520113 100644 --- a/content/html/document/src/nsHTMLContentSink.cpp +++ b/content/html/document/src/nsHTMLContentSink.cpp @@ -2950,6 +2950,13 @@ HTMLContentSink::ProcessLINKTag(const nsIParserNode& aNode) PrefetchHref(hrefVal, element, hasPrefetch); } } + if (linkTypes.IndexOf(NS_LITERAL_STRING("dns-prefetch")) != -1) { + nsAutoString hrefVal; + element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); + if (!hrefVal.IsEmpty()) { + PrefetchDNS(hrefVal); + } + } } } } diff --git a/content/xml/document/src/nsXMLContentSink.cpp b/content/xml/document/src/nsXMLContentSink.cpp index 7c5c5000fa18..56f1ddb7e1dc 100644 --- a/content/xml/document/src/nsXMLContentSink.cpp +++ b/content/xml/document/src/nsXMLContentSink.cpp @@ -657,6 +657,18 @@ nsXMLContentSink::CloseElement(nsIContent* aContent) mScriptLoader->AddExecuteBlocker(); } } + // Look for + if (nodeInfo->Equals(nsGkAtoms::link, kNameSpaceID_XHTML)) { + nsAutoString relVal; + aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal); + if (relVal.EqualsLiteral("dns-prefetch")) { + nsAutoString hrefVal; + aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal); + if (!hrefVal.IsEmpty()) { + PrefetchDNS(hrefVal); + } + } + } } return rv; diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index e9b81452aa1d..de9804b745f0 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -83,6 +83,7 @@ #include "nsXMLHttpRequest.h" #include "nsIFocusEventSuppressor.h" #include "nsDOMThreadService.h" +#include "nsHTMLDNSPrefetch.h" #ifdef MOZ_XUL #include "nsXULPopupManager.h" @@ -185,6 +186,12 @@ nsLayoutStatics::Initialize() return rv; } + rv = nsHTMLDNSPrefetch::Initialize(); + if (NS_FAILED(rv)) { + NS_ERROR("Could not initialize HTML DNS prefetch"); + return rv; + } + #ifdef MOZ_XUL rv = nsXULContentUtils::Init(); if (NS_FAILED(rv)) { @@ -280,6 +287,7 @@ nsLayoutStatics::Shutdown() CSSLoaderImpl::Shutdown(); nsCSSRuleProcessor::FreeSystemMetrics(); nsTextFrameTextRunCache::Shutdown(); + nsHTMLDNSPrefetch::Shutdown(); nsCSSRendering::Shutdown(); #ifdef DEBUG nsFrame::DisplayReflowShutdown(); diff --git a/netwerk/base/public/nsNetError.h b/netwerk/base/public/nsNetError.h index f3f8bda3d487..b9bac3de8f8e 100644 --- a/netwerk/base/public/nsNetError.h +++ b/netwerk/base/public/nsNetError.h @@ -272,6 +272,14 @@ #define NS_ERROR_UNKNOWN_HOST \ NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 30) +/** + * A low or medium priority DNS lookup failed because the pending + * queue was already full. High priorty (the default) always + * makes room + */ +#define NS_ERROR_DNS_LOOKUP_QUEUE_FULL \ + NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_NETWORK, 33) + /** * The lookup of a proxy hostname failed. * diff --git a/netwerk/base/src/Makefile.in b/netwerk/base/src/Makefile.in index 4c2dea5de2dd..2058b7a39f1f 100644 --- a/netwerk/base/src/Makefile.in +++ b/netwerk/base/src/Makefile.in @@ -93,6 +93,7 @@ CPPSRCS = \ nsNetStrings.cpp \ nsBase64Encoder.cpp \ nsSerializationHelper.cpp \ + nsDNSPrefetch.cpp \ $(NULL) ifeq ($(MOZ_WIDGET_TOOLKIT),os2) diff --git a/netwerk/build/nsNetModule.cpp b/netwerk/build/nsNetModule.cpp index 6e894aea381f..6c573c6d6b77 100644 --- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -59,6 +59,7 @@ #include "nsDiskCacheDeviceSQL.h" #include "nsMimeTypes.h" #include "nsNetStrings.h" +#include "nsDNSPrefetch.h" #include "nsNetCID.h" @@ -619,10 +620,13 @@ static void nsNetShutdown(nsIModule *neckoModule) #ifdef XP_MACOSX net_ShutdownURLHelperOSX(); #endif - + // Release necko strings delete gNetStrings; gNetStrings = nsnull; + + // Release DNS service reference. + nsDNSPrefetch::Shutdown(); } static const nsModuleComponentInfo gNetModuleInfo[] = { diff --git a/netwerk/dns/public/nsIDNSService.idl b/netwerk/dns/public/nsIDNSService.idl index d7d5e135c2b2..522361a1c997 100644 --- a/netwerk/dns/public/nsIDNSService.idl +++ b/netwerk/dns/public/nsIDNSService.idl @@ -46,7 +46,7 @@ interface nsIDNSListener; /** * nsIDNSService */ -[scriptable, uuid(3ac9e611-e6b6-44b5-b312-c040e65b2929)] +[scriptable, uuid(ee4d9f1d-4f99-4384-b547-29da735f8b6e)] interface nsIDNSService : nsISupports { /** @@ -107,4 +107,11 @@ interface nsIDNSService : nsISupports * if set, the canonical name of the specified host will be queried. */ const unsigned long RESOLVE_CANONICAL_NAME = (1 << 1); + + /** + * if set, the query is given lower priority. Medium takes precedence + * if both are used. + */ + const unsigned long RESOLVE_PRIORITY_MEDIUM = (1 << 2); + const unsigned long RESOLVE_PRIORITY_LOW = (1 << 3); }; diff --git a/netwerk/dns/src/nsDNSService2.cpp b/netwerk/dns/src/nsDNSService2.cpp index a6b8a75175e2..ed4eecb3af08 100644 --- a/netwerk/dns/src/nsDNSService2.cpp +++ b/netwerk/dns/src/nsDNSService2.cpp @@ -50,6 +50,7 @@ #include "nsAutoPtr.h" #include "nsNetCID.h" #include "nsNetError.h" +#include "nsDNSPrefetch.h" #include "prsystem.h" #include "prnetdb.h" #include "prmon.h" @@ -61,6 +62,7 @@ static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration"; static const char kPrefEnableIDN[] = "network.enableIDN"; static const char kPrefIPv4OnlyDomains[] = "network.dns.ipv4OnlyDomains"; static const char kPrefDisableIPv6[] = "network.dns.disableIPv6"; +static const char kPrefDisablePrefetch[] = "network.dns.disablePrefetch"; //----------------------------------------------------------------------------- @@ -321,10 +323,12 @@ nsDNSService::Init() PRBool firstTime = (mLock == nsnull); // prefs - PRUint32 maxCacheEntries = 20; - PRUint32 maxCacheLifetime = 1; // minutes + PRUint32 maxCacheEntries = 400; + PRUint32 maxCacheLifetime = 3; // minutes PRBool enableIDN = PR_TRUE; PRBool disableIPv6 = PR_FALSE; + PRBool disablePrefetch = PR_FALSE; + nsAdoptingCString ipv4OnlyDomains; // read prefs @@ -340,6 +344,7 @@ nsDNSService::Init() prefs->GetBoolPref(kPrefEnableIDN, &enableIDN); prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6); prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains)); + prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch); } if (firstTime) { @@ -354,6 +359,7 @@ nsDNSService::Init() prefs->AddObserver(kPrefEnableIDN, this, PR_FALSE); prefs->AddObserver(kPrefIPv4OnlyDomains, this, PR_FALSE); prefs->AddObserver(kPrefDisableIPv6, this, PR_FALSE); + prefs->AddObserver(kPrefDisablePrefetch, this, PR_FALSE); } } @@ -374,8 +380,10 @@ nsDNSService::Init() mIDN = idn; mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership mDisableIPv6 = disableIPv6; + mDisablePrefetch = disablePrefetch; } - + + nsDNSPrefetch::Initialize(this); return rv; } @@ -406,6 +414,10 @@ nsDNSService::AsyncResolve(const nsACString &hostname, nsCOMPtr idn; { nsAutoLock lock(mLock); + + if (mDisablePrefetch && (flags & (RESOLVE_PRIORITY_LOW | RESOLVE_PRIORITY_MEDIUM))) + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + res = mResolver; idn = mIDN; } diff --git a/netwerk/dns/src/nsDNSService2.h b/netwerk/dns/src/nsDNSService2.h index e0e0b34285a9..9487d7836a38 100644 --- a/netwerk/dns/src/nsDNSService2.h +++ b/netwerk/dns/src/nsDNSService2.h @@ -68,4 +68,5 @@ private: // a per-domain basis and work around broken DNS servers. See bug 68796. nsAdoptingCString mIPv4OnlyDomains; PRBool mDisableIPv6; + PRBool mDisablePrefetch; }; diff --git a/netwerk/dns/src/nsHostResolver.cpp b/netwerk/dns/src/nsHostResolver.cpp index 5b5629cd6e54..f5edc0c3e634 100644 --- a/netwerk/dns/src/nsHostResolver.cpp +++ b/netwerk/dns/src/nsHostResolver.cpp @@ -68,8 +68,28 @@ //---------------------------------------------------------------------------- -#define MAX_THREADS 8 -#define IDLE_TIMEOUT PR_SecondsToInterval(60) +// Use a persistent thread pool in order to avoid spinning up new threads all the time. +// In particular, thread creation results in a res_init() call from libc which is +// quite expensive. +// +// The pool dynamically grows between 0 and MAX_RESOLVER_THREADS in size. New requests +// go first to an idle thread. If that cannot be found and there are fewer than MAX_RESOLVER_THREADS +// currently in the pool a new thread is created for high priority requests. If +// the new request is at a lower priority a new thread will only be created if +// there are fewer than HighThreadThreshold currently outstanding. If a thread cannot be +// created or an idle thread located for the request it is queued. +// +// When the pool is greater than HighThreadThreshold in size a thread will be destroyed after +// ShortIdleTimeoutSeconds of idle time. Smaller pools use LongIdleTimeoutSeconds for a +// timeout period. + +#define MAX_NON_PRIORITY_REQUESTS 150 + +#define HighThreadThreshold 4 +#define LongIdleTimeoutSeconds 300 // for threads 1 -> HighThreadThreshold +#define ShortIdleTimeoutSeconds 60 // for threads HighThreadThreshold+1 -> MAX_RESOLVER_THREADS + +PR_STATIC_ASSERT (HighThreadThreshold <= MAX_RESOLVER_THREADS); //---------------------------------------------------------------------------- @@ -184,6 +204,7 @@ nsHostRecord::Create(const nsHostKey *key, nsHostRecord **result) rec->addr = nsnull; rec->expiration = NowInMinutes(); rec->resolving = PR_FALSE; + rec->onQueue = PR_FALSE; PR_INIT_CLIST(rec); PR_INIT_CLIST(&rec->callbacks); rec->negative = PR_FALSE; @@ -308,14 +329,26 @@ nsHostResolver::nsHostResolver(PRUint32 maxCacheEntries, , mMaxCacheLifetime(maxCacheLifetime) , mLock(nsnull) , mIdleThreadCV(nsnull) - , mHaveIdleThread(PR_FALSE) + , mNumIdleThreads(0) , mThreadCount(0) + , mAnyPriorityThreadCount(0) , mEvictionQSize(0) + , mPendingCount(0) , mShutdown(PR_TRUE) { mCreationTime = PR_Now(); - PR_INIT_CLIST(&mPendingQ); + PR_INIT_CLIST(&mHighQ); + PR_INIT_CLIST(&mMediumQ); + PR_INIT_CLIST(&mLowQ); PR_INIT_CLIST(&mEvictionQ); + + mHighPriorityInfo.self = this; + mHighPriorityInfo.onlyHighPriority = PR_TRUE; + mAnyPriorityInfo.self = this; + mAnyPriorityInfo.onlyHighPriority = PR_FALSE; + + mLongIdleTimeout = PR_SecondsToInterval(LongIdleTimeoutSeconds); + mShortIdleTimeout = PR_SecondsToInterval(ShortIdleTimeoutSeconds); } nsHostResolver::~nsHostResolver() @@ -359,13 +392,29 @@ nsHostResolver::Init() return NS_OK; } +void +nsHostResolver::ClearPendingQueue(PRCList *aPendingQ) +{ + // loop through pending queue, erroring out pending lookups. + if (!PR_CLIST_IS_EMPTY(aPendingQ)) { + PRCList *node = aPendingQ->next; + while (node != aPendingQ) { + nsHostRecord *rec = static_cast(node); + node = node->next; + OnLookupComplete(rec, NS_ERROR_ABORT, nsnull); + } + } +} + void nsHostResolver::Shutdown() { LOG(("nsHostResolver::Shutdown\n")); - PRCList pendingQ, evictionQ; - PR_INIT_CLIST(&pendingQ); + PRCList pendingQHigh, pendingQMed, pendingQLow, evictionQ; + PR_INIT_CLIST(&pendingQHigh); + PR_INIT_CLIST(&pendingQMed); + PR_INIT_CLIST(&pendingQLow); PR_INIT_CLIST(&evictionQ); { @@ -373,26 +422,23 @@ nsHostResolver::Shutdown() mShutdown = PR_TRUE; - MoveCList(mPendingQ, pendingQ); + MoveCList(mHighQ, pendingQHigh); + MoveCList(mMediumQ, pendingQMed); + MoveCList(mLowQ, pendingQLow); MoveCList(mEvictionQ, evictionQ); mEvictionQSize = 0; - - if (mHaveIdleThread) + mPendingCount = 0; + + if (mNumIdleThreads) PR_NotifyCondVar(mIdleThreadCV); // empty host database PL_DHashTableEnumerate(&mDB, HostDB_RemoveEntry, nsnull); } - - // loop through pending queue, erroring out pending lookups. - if (!PR_CLIST_IS_EMPTY(&pendingQ)) { - PRCList *node = pendingQ.next; - while (node != &pendingQ) { - nsHostRecord *rec = static_cast(node); - node = node->next; - OnLookupComplete(rec, NS_ERROR_ABORT, nsnull); - } - } + + ClearPendingQueue(&pendingQHigh); + ClearPendingQueue(&pendingQMed); + ClearPendingQueue(&pendingQLow); if (!PR_CLIST_IS_EMPTY(&evictionQ)) { PRCList *node = evictionQ.next; @@ -405,6 +451,33 @@ nsHostResolver::Shutdown() } +static inline PRBool +IsHighPriority(PRUint16 flags) +{ + return !(flags & (nsHostResolver::RES_PRIORITY_LOW | nsHostResolver::RES_PRIORITY_MEDIUM)); +} + +static inline PRBool +IsMediumPriority(PRUint16 flags) +{ + return flags & nsHostResolver::RES_PRIORITY_MEDIUM; +} + +static inline PRBool +IsLowPriority(PRUint16 flags) +{ + return flags & nsHostResolver::RES_PRIORITY_LOW; +} + +void +nsHostResolver::MoveQueue(nsHostRecord *aRec, PRCList &aDestQ) +{ + NS_ASSERTION(aRec->onQueue, "Moving Host Record Not Currently Queued"); + + PR_REMOVE_LINK(aRec); + PR_APPEND_LINK(aRec, &aDestQ); +} + nsresult nsHostResolver::ResolveHost(const char *host, PRUint16 flags, @@ -484,9 +557,15 @@ nsHostResolver::ResolveHost(const char *host, // put reference to host record on stack... result = he->rec; } + else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS && + !IsHighPriority(flags) && + !he->rec->resolving) { + // This is a lower priority request and we are swamped, so refuse it. + rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + } // otherwise, hit the resolver... else { - // add callback to the list of pending callbacks + // Add callback to the list of pending callbacks. PR_APPEND_LINK(callback, &he->rec->callbacks); if (!he->rec->resolving) { @@ -495,6 +574,21 @@ nsHostResolver::ResolveHost(const char *host, if (NS_FAILED(rv)) PR_REMOVE_AND_INIT_LINK(callback); } + else if (he->rec->onQueue) { + // Consider the case where we are on a pending queue of + // lower priority than the request is being made at. + // In that case we should upgrade to the higher queue. + + if (IsHighPriority(flags) && !IsHighPriority(he->rec->flags)) { + // Move from (low|med) to high. + MoveQueue(he->rec, mHighQ); + he->rec->flags = flags; + } else if (IsMediumPriority(flags) && IsLowPriority(he->rec->flags)) { + // Move from low to med. + MoveQueue(he->rec, mMediumQ); + he->rec->flags = flags; + } + } } } } @@ -543,35 +637,57 @@ nsHostResolver::IssueLookup(nsHostRecord *rec) { NS_ASSERTION(!rec->resolving, "record is already being resolved"); - // add rec to mPendingQ, possibly removing it from mEvictionQ. - // if rec is on mEvictionQ, then we can just move the owning - // reference over to mPendingQ. + // Add rec to one of the pending queues, possibly removing it from mEvictionQ. + // If rec is on mEvictionQ, then we can just move the owning + // reference over to the new active queue. if (rec->next == rec) NS_ADDREF(rec); else { PR_REMOVE_LINK(rec); mEvictionQSize--; } - PR_APPEND_LINK(rec, &mPendingQ); + + if (IsHighPriority(rec->flags)) + PR_APPEND_LINK(rec, &mHighQ); + else if (IsMediumPriority(rec->flags)) + PR_APPEND_LINK(rec, &mMediumQ); + else + PR_APPEND_LINK(rec, &mLowQ); + mPendingCount++; + rec->resolving = PR_TRUE; + rec->onQueue = PR_TRUE; - if (mHaveIdleThread) { + if (mNumIdleThreads) { // wake up idle thread to process this lookup PR_NotifyCondVar(mIdleThreadCV); } - else if (mThreadCount < MAX_THREADS) { + else if ((mThreadCount < HighThreadThreshold) || + (IsHighPriority(rec->flags) && mThreadCount < MAX_RESOLVER_THREADS)) { // dispatch new worker thread NS_ADDREF_THIS(); // owning reference passed to thread + + struct nsHostResolverThreadInfo *info; + + if (mAnyPriorityThreadCount < HighThreadThreshold) { + info = &mAnyPriorityInfo; + mAnyPriorityThreadCount++; + } + else + info = &mHighPriorityInfo; + mThreadCount++; PRThread *thr = PR_CreateThread(PR_SYSTEM_THREAD, ThreadFunc, - this, + info, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0); if (!thr) { mThreadCount--; + if (info == &mAnyPriorityInfo) + mAnyPriorityThreadCount--; NS_RELEASE_THIS(); return NS_ERROR_OUT_OF_MEMORY; } @@ -584,25 +700,54 @@ nsHostResolver::IssueLookup(nsHostRecord *rec) return NS_OK; } +void +nsHostResolver::DeQueue(PRCList &aQ, nsHostRecord **aResult) +{ + *aResult = static_cast(aQ.next); + PR_REMOVE_AND_INIT_LINK(*aResult); + mPendingCount--; + (*aResult)->onQueue = PR_FALSE; +} + PRBool -nsHostResolver::GetHostToLookup(nsHostRecord **result) +nsHostResolver::GetHostToLookup(nsHostRecord **result, struct nsHostResolverThreadInfo *aID) { nsAutoLock lock(mLock); - PRIntervalTime start = PR_IntervalNow(), timeout = IDLE_TIMEOUT; - // - // wait for one or more of the following to occur: - // (1) the pending queue has a host record to process - // (2) the shutdown flag has been set - // (3) the thread has been idle for too long - // - // PR_WaitCondVar will return when any of these conditions is true. - // - while (PR_CLIST_IS_EMPTY(&mPendingQ) && !mHaveIdleThread && !mShutdown) { + PRIntervalTime start = PR_IntervalNow(), timeout; + + while (!mShutdown) { + // remove next record from Q; hand over owning reference. Check high, then med, then low + + if (!PR_CLIST_IS_EMPTY(&mHighQ)) { + DeQueue (mHighQ, result); + return PR_TRUE; + } + + if (! aID->onlyHighPriority) { + if (!PR_CLIST_IS_EMPTY(&mMediumQ)) { + DeQueue (mMediumQ, result); + return PR_TRUE; + } + + if (!PR_CLIST_IS_EMPTY(&mLowQ)) { + DeQueue (mLowQ, result); + return PR_TRUE; + } + } + + timeout = (mNumIdleThreads >= HighThreadThreshold) ? mShortIdleTimeout : mLongIdleTimeout; + // wait for one or more of the following to occur: + // (1) the pending queue has a host record to process + // (2) the shutdown flag has been set + // (3) the thread has been idle for too long + // + // PR_WaitCondVar will return when any of these conditions is true. // become the idle thread and wait for a lookup - mHaveIdleThread = PR_TRUE; + + mNumIdleThreads++; PR_WaitCondVar(mIdleThreadCV, timeout); - mHaveIdleThread = PR_FALSE; + mNumIdleThreads--; PRIntervalTime delta = PR_IntervalNow() - start; if (delta >= timeout) @@ -611,15 +756,10 @@ nsHostResolver::GetHostToLookup(nsHostRecord **result) start += delta; } - if (!PR_CLIST_IS_EMPTY(&mPendingQ)) { - // remove next record from mPendingQ; hand over owning reference. - *result = static_cast(mPendingQ.next); - PR_REMOVE_AND_INIT_LINK(*result); - return PR_TRUE; - } - // tell thread to exit... mThreadCount--; + if (!aID->onlyHighPriority) + mAnyPriorityThreadCount--; return PR_FALSE; } @@ -698,11 +838,11 @@ nsHostResolver::ThreadFunc(void *arg) #if defined(RES_RETRY_ON_FAILURE) nsResState rs; #endif - - nsHostResolver *resolver = (nsHostResolver *) arg; + struct nsHostResolverThreadInfo *info = (struct nsHostResolverThreadInfo *) arg; + nsHostResolver *resolver = info->self; nsHostRecord *rec; PRAddrInfo *ai; - while (resolver->GetHostToLookup(&rec)) { + while (resolver->GetHostToLookup(&rec, info)) { LOG(("resolving %s ...\n", rec->host)); PRIntn flags = PR_AI_ADDRCONFIG; diff --git a/netwerk/dns/src/nsHostResolver.h b/netwerk/dns/src/nsHostResolver.h index cd6c43523a87..0baa56edb87a 100644 --- a/netwerk/dns/src/nsHostResolver.h +++ b/netwerk/dns/src/nsHostResolver.h @@ -68,6 +68,11 @@ class nsResolveHostCallback; return n; \ } +#define MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY 5 +#define MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY 3 +#define MAX_RESOLVER_THREADS (MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY + \ + MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY) + struct nsHostKey { const char *host; @@ -124,6 +129,9 @@ private: PRBool resolving; /* true if this record is being resolved, which means * that it is either on the pending queue or owned by * one of the worker threads. */ + + PRBool onQueue; /* true if pending and on the queue (not yet given to getaddrinfo())*/ + ~nsHostRecord(); }; @@ -157,6 +165,16 @@ public: nsresult status) = 0; }; +/** + * nsHostResolverThreadInfo structures are passed to the resolver + * thread. + */ +struct nsHostResolverThreadInfo +{ + nsHostResolver *self; + PRBool onlyHighPriority; +}; + /** * nsHostResolver - an asynchronous host name resolver. */ @@ -214,32 +232,47 @@ public: */ enum { RES_BYPASS_CACHE = 1 << 0, - RES_CANON_NAME = 1 << 1 + RES_CANON_NAME = 1 << 1, + RES_PRIORITY_MEDIUM = 1 << 2, + RES_PRIORITY_LOW = 1 << 3 }; private: nsHostResolver(PRUint32 maxCacheEntries=50, PRUint32 maxCacheLifetime=1); ~nsHostResolver(); + // nsHostResolverThreadInfo * is passed to the ThreadFunc + struct nsHostResolverThreadInfo mHighPriorityInfo, mAnyPriorityInfo; + nsresult Init(); nsresult IssueLookup(nsHostRecord *); - PRBool GetHostToLookup(nsHostRecord **); + PRBool GetHostToLookup(nsHostRecord **m, struct nsHostResolverThreadInfo *aID); void OnLookupComplete(nsHostRecord *, nsresult, PRAddrInfo *); + void DeQueue(PRCList &aQ, nsHostRecord **aResult); + void ClearPendingQueue(PRCList *aPendingQueue); + static void MoveQueue(nsHostRecord *aRec, PRCList &aDestQ); + static void ThreadFunc(void *); PRUint32 mMaxCacheEntries; PRUint32 mMaxCacheLifetime; PRLock *mLock; PRCondVar *mIdleThreadCV; // non-null if idle thread - PRBool mHaveIdleThread; + PRUint32 mNumIdleThreads; PRUint32 mThreadCount; + PRUint32 mAnyPriorityThreadCount; PLDHashTable mDB; - PRCList mPendingQ; + PRCList mHighQ; + PRCList mMediumQ; + PRCList mLowQ; PRCList mEvictionQ; PRUint32 mEvictionQSize; + PRUint32 mPendingCount; PRTime mCreationTime; PRBool mShutdown; + PRIntervalTime mLongIdleTimeout; + PRIntervalTime mShortIdleTimeout; }; #endif // nsHostResolver_h__ diff --git a/netwerk/protocol/http/src/nsHttpChannel.cpp b/netwerk/protocol/http/src/nsHttpChannel.cpp index adbc595d03a7..16c61da99c5e 100644 --- a/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -81,6 +81,7 @@ #include "nsIOService.h" #include "nsAuthInformationHolder.h" #include "nsICacheService.h" +#include "nsDNSPrefetch.h" // True if the local cache should be bypassed when processing a request. #define BYPASS_LOCAL_CACHE(loadFlags) \ @@ -3993,6 +3994,13 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) if (NS_FAILED(rv)) return rv; + // Start a DNS lookup very early in case the real open is queued the DNS can + // happen in parallel. + nsRefPtr prefetch = new nsDNSPrefetch(mURI); + if (prefetch) { + prefetch->PrefetchMedium(); + } + // Remember the cookie header that was set, if any const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie); if (cookieHeader) From 322a9a7af8cb801747fa99ab8983ddc2c3b24467 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 4 Nov 2008 12:03:11 -0500 Subject: [PATCH 10/10] Bug 453403. Fix leak of nsHostResolver by making sure to wake up all the worker threads on shutdown --- netwerk/dns/src/nsHostResolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netwerk/dns/src/nsHostResolver.cpp b/netwerk/dns/src/nsHostResolver.cpp index f5edc0c3e634..b5b3aac221b2 100644 --- a/netwerk/dns/src/nsHostResolver.cpp +++ b/netwerk/dns/src/nsHostResolver.cpp @@ -430,7 +430,7 @@ nsHostResolver::Shutdown() mPendingCount = 0; if (mNumIdleThreads) - PR_NotifyCondVar(mIdleThreadCV); + PR_NotifyAllCondVar(mIdleThreadCV); // empty host database PL_DHashTableEnumerate(&mDB, HostDB_RemoveEntry, nsnull);