Make wrapper preservation (the mechanism that makes the GC use reachability information between certain C++ objects rather than rooting at language boundaries) use an interface (nsIDOMGCParticipant) to get reachability information. Preserve the wrappers for event handlers as long as what they are attached to is reachable (from C++ or JS) to avoid entraining event handler closures in cycles. b=241518 r=mrbkap sr=jst

This commit is contained in:
dbaron%dbaron.org 2005-12-03 07:42:40 +00:00
Родитель e79a259e56
Коммит 3ceffc0fc4
34 изменённых файлов: 683 добавлений и 250 удалений

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

@ -56,6 +56,7 @@ nsContentUtils.h \
nsIDocument.h \
nsIDocumentEncoder.h \
nsIDocumentObserver.h \
nsIDOMGCParticipant.h \
nsINameSpaceManager.h \
nsINodeInfo.h \
nsIRangeUtils.h \

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

@ -44,6 +44,7 @@
#include "nsINodeInfo.h"
#include "nsIContent.h"
#include "nsPropertyTable.h"
#include "nsIDOMGCParticipant.h"
class nsIAtom;
class nsDOMAttributeMap;
@ -52,7 +53,7 @@ class nsDOMAttributeMap;
{0x4940cc50, 0x2ede, 0x4883, \
{0x95, 0xf5, 0x53, 0xdb, 0x50, 0x50, 0x13, 0x3e}}
class nsIAttribute : public nsISupports
class nsIAttribute : public nsIDOMGCParticipant
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IATTRIBUTE_IID)

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

@ -46,6 +46,7 @@
#include "nsPropertyTable.h"
#include "nsCaseTreatment.h"
#include "nsChangeHint.h"
#include "nsIDOMGCParticipant.h"
#ifdef MOZILLA_INTERNAL_API
#include "nsINodeInfo.h"
@ -67,15 +68,16 @@ class nsRuleWalker;
class nsAttrValue;
// IID for the nsIContent interface
#define NS_ICONTENT_IID \
{ 0x5d098839, 0x389d, 0x41db, \
{ 0x8f, 0x53, 0x59, 0x07, 0xbf, 0x90, 0x0d, 0x4e } }
// ffc6f2b8-bcdc-4cf7-b72f-e843860f14a6
#define NS_ICONTENT_IID \
{ 0xffc6f2b8, 0xbcdc, 0x4cf7, \
{ 0xb7, 0x2f, 0xe8, 0x43, 0x86, 0x0f, 0x14, 0xa6 } }
/**
* A node of content in a document's content model. This interface
* is supported by all content objects.
*/
class nsIContent : public nsISupports {
class nsIContent : public nsIDOMGCParticipant {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENT_IID)

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

@ -0,0 +1,87 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of 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 nsIDOMGCParticipant_h_
#define nsIDOMGCParticipant_h_
#include "nscore.h"
#include "nsISupports.h"
#include "nsCOMArray.h"
// 0e2a5a8d-28fd-4a5c-8bf1-5b0067ff3286
#define NS_IDOMGCPARTICIPANT_IID \
{ 0x0e2a5a8d, 0x28fd, 0x4a5c, \
{0x8b, 0xf1, 0x5b, 0x00, 0x67, 0xff, 0x32, 0x86} }
/**
* DOM GC Participants are objects that expose information about
* reachability in the native object graphs to help prevent script ->
* native -> script cyclical reference from causing leaks due to the
* creation of garbage collection roots and native/script boundaries.
*/
class nsIDOMGCParticipant : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOMGCPARTICIPANT_IID)
/**
* Get a reference node for what is known to be a strongly connected
* component of nsIDOMGCParticipants. For example, DOM trees are
* strongly connected, so can return the root node to greatly reduce
* the number of nodes on which we need to run graph algorithms.
*
* Note that it's acceptable for nodes in a single strongly connected
* component to return different values for GetSCCIndex, as long as
* those two values claim that they're reachable from each other in
* AppendReachableList.
*/
virtual nsIDOMGCParticipant* GetSCCIndex() = 0;
/**
* Append the list of nsIDOMGCPartipants reachable from this one via
* C++ getters exposed to script that return a different result from
* |GetSCCIndex|. The caller is responsible for taking the transitive
* closure of |AppendReachableList|.
*
* This will only be called on objects that are returned by GetSCCIndex.
*/
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDOMGCParticipant, NS_IDOMGCPARTICIPANT_IID)
#endif // !defined(nsIDOMGCParticipant_h_)

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

@ -37,7 +37,7 @@
#ifndef nsIDocument_h___
#define nsIDocument_h___
#include "nsISupports.h"
#include "nsIDOMGCParticipant.h"
#include "nsEvent.h"
#include "nsStringGlue.h"
#include "nsCOMArray.h"
@ -92,9 +92,10 @@ class nsIVariant;
class nsIDOMUserDataHandler;
// IID for the nsIDocument interface
#define NS_IDOCUMENT_IID \
{ 0xbcb48147, 0xed60, 0x490e, \
{ 0xa2, 0x47, 0xe2, 0x35, 0x3c, 0xf7, 0xc8, 0x68 } }
// a5d8343d-9b0a-40a8-a47e-893065749f0b
#define NS_IDOCUMENT_IID \
{ 0xa5d8343d, 0x9b0a, 0x40a8, \
{ 0xa4, 0x7e, 0x89, 0x30, 0x65, 0x74, 0x9f, 0x0b } }
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
@ -103,7 +104,7 @@ class nsIDOMUserDataHandler;
// Document interface. This is implemented by all document objects in
// Gecko.
class nsIDocument : public nsISupports
class nsIDocument : public nsIDOMGCParticipant
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOCUMENT_IID)

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

@ -83,6 +83,7 @@ nsDOMAttribute::~nsDOMAttribute()
NS_INTERFACE_MAP_BEGIN(nsDOMAttribute)
NS_INTERFACE_MAP_ENTRY(nsIDOMAttr)
NS_INTERFACE_MAP_ENTRY(nsIAttribute)
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
NS_INTERFACE_MAP_ENTRY(nsIDOMNode)
NS_INTERFACE_MAP_ENTRY(nsIDOM3Node)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMAttr)
@ -93,6 +94,22 @@ NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(nsDOMAttribute)
NS_IMPL_RELEASE(nsDOMAttribute)
// nsIDOMGCParticipant methods
nsIDOMGCParticipant*
nsDOMAttribute::GetSCCIndex()
{
PRBool spec;
if (NS_SUCCEEDED(GetSpecified(&spec)) && spec) {
return GetContentInternal()->GetSCCIndex();
}
return this;
}
void
nsDOMAttribute::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
{
}
void
nsDOMAttribute::SetMap(nsDOMAttributeMap *aMap)
{

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

@ -80,6 +80,10 @@ public:
NS_DECL_ISUPPORTS
// nsIDOMGCParticipant interface methods
virtual nsIDOMGCParticipant* GetSCCIndex();
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
// nsIDOMNode interface
NS_DECL_NSIDOMNODE

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

@ -797,6 +797,7 @@ NS_INTERFACE_MAP_BEGIN(nsDocument)
NS_INTERFACE_MAP_ENTRY(nsIDOMDocumentXBL)
NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventReceiver)
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
NS_INTERFACE_MAP_ENTRY(nsIDOMNSEventTarget)
@ -3324,6 +3325,23 @@ nsDocument::SetDir(const nsAString& aDirection)
return NS_OK;
}
//
// nsIDOMGCParticipant methods
//
nsIDOMGCParticipant*
nsDocument::GetSCCIndex()
{
return this;
}
void
nsDocument::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
{
nsCOMPtr<nsIDOMGCParticipant> gcp = do_QueryInterface(mScriptGlobalObject);
if (gcp)
aArray.AppendObject(gcp);
}
//
// nsIDOMNode methods

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

@ -557,6 +557,10 @@ public:
nsresult GetRadioGroup(const nsAString& aName,
nsRadioGroupStruct **aRadioGroup);
// nsIDOMGCParticipant interface methods
virtual nsIDOMGCParticipant* GetSCCIndex();
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
// nsIDOMNode
NS_DECL_NSIDOMNODE

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

@ -94,6 +94,7 @@ NS_IMPL_RELEASE(nsGenericDOMDataNode)
NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventReceiver,
nsDOMEventRTTearoff::Create(this))
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventTarget,
@ -632,6 +633,30 @@ nsGenericDOMDataNode::ToCString(nsAString& aBuf, PRInt32 aOffset,
}
#endif
/**
* See comment for nsGenericElement::GetSCCIndex
*/
nsIDOMGCParticipant*
nsGenericDOMDataNode::GetSCCIndex()
{
// This is an optimized way of walking nsIDOMNode::GetParentNode to
// the top of the tree.
nsIDOMGCParticipant *result = GetCurrentDoc();
if (!result) {
nsIContent *top = this;
while (top->GetParent())
top = top->GetParent();
result = top;
}
return result;
}
void
nsGenericDOMDataNode::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
{
}
nsresult
nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,

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

@ -174,6 +174,10 @@ public:
nsresult ReplaceData(PRUint32 aOffset, PRUint32 aCount,
const nsAString& aArg);
// nsIDOMGCParticipant interface methods
virtual nsIDOMGCParticipant* GetSCCIndex();
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
// Implementation for nsIContent
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,

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

@ -1017,6 +1017,37 @@ nsGenericElement::InitHashes()
return NS_OK;
}
/**
* During the Mark phase of the GC, we need to mark all of the preserved
* wrappers that are reachable via DOM APIs. Since reachability for DOM
* nodes is symmetric, if one DOM node is reachable from another via DOM
* APIs, then they are in the same strongly connected component.
* (Strongly connected components are never reachable from each other
* via DOM APIs.) We can refer to each strongly connected component by
* walking up to the top of the parent chain. This function finds that
* root node for any DOM node.
*/
nsIDOMGCParticipant*
nsGenericElement::GetSCCIndex()
{
// This is an optimized way of walking nsIDOMNode::GetParentNode to
// the top of the tree.
nsIDOMGCParticipant *result = GetCurrentDoc();
if (!result) {
nsIContent *top = this;
while (top->GetParent())
top = top->GetParent();
result = top;
}
return result;
}
void
nsGenericElement::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
{
}
NS_IMETHODIMP
nsGenericElement::GetNodeName(nsAString& aNodeName)
{
@ -3694,6 +3725,7 @@ nsGenericElement::RemoveChild(nsIDOMNode *aOldChild, nsIDOMNode **aReturn)
NS_INTERFACE_MAP_BEGIN(nsGenericElement)
NS_INTERFACE_MAP_ENTRY(nsIContent)
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Node, new nsNode3Tearoff(this))
NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMEventReceiver,
nsDOMEventRTTearoff::Create(this))

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

@ -352,6 +352,10 @@ public:
/** Free globals, to be called from module destructor */
static void Shutdown();
// nsIDOMGCParticipant interface methods
virtual nsIDOMGCParticipant* GetSCCIndex();
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
// nsIContent interface methods
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,

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

@ -419,14 +419,12 @@ GenericListenersHashEnum(nsHashKey *aKey, void *aData, void* closure)
if (ls) {
if (*scriptOnly) {
if (ls->mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) {
NS_RELEASE(ls->mListener);
//listeners->RemoveElement((void*)ls); we delete the entire array anyways, no need to RemoveElement
PR_DELETE(ls);
listeners->RemoveElement(ls);
delete ls;
}
}
else {
NS_IF_RELEASE(ls->mListener);
PR_DELETE(ls);
delete ls;
}
}
}
@ -686,14 +684,12 @@ nsEventListenerManager::ReleaseListeners(nsVoidArray** aListeners,
if (ls) {
if (aScriptOnly) {
if (ls->mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) {
NS_RELEASE(ls->mListener);
//(*aListeners)->RemoveElement((void*)ls); We're going to delete the array anyways
PR_DELETE(ls);
(*aListeners)->RemoveElement(ls);
delete ls;
}
}
else {
NS_IF_RELEASE(ls->mListener);
PR_DELETE(ls);
delete ls;
}
}
}
@ -765,7 +761,8 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
for (PRInt32 i=0; i<listeners->Count(); i++) {
ls = (nsListenerStruct*)listeners->ElementAt(i);
if (ls->mListener == aListener && ls->mFlags == aFlags &&
nsRefPtr<nsIDOMEventListener> iListener = ls->mListener.Get();
if (iListener == aListener && ls->mFlags == aFlags &&
ls->mGroupFlags == group) {
ls->mSubType |= aSubType;
found = PR_TRUE;
@ -774,17 +771,20 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
}
if (!found) {
ls = PR_NEW(nsListenerStruct);
if (ls) {
ls->mListener = aListener;
ls->mFlags = aFlags;
ls->mSubType = aSubType;
ls->mSubTypeCapture = NS_EVENT_BITS_NONE;
ls->mHandlerIsString = 0;
ls->mGroupFlags = group;
listeners->AppendElement((void*)ls);
NS_ADDREF(aListener);
ls = new nsListenerStruct;
if (!ls) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCOMPtr<nsIDOMGCParticipant> participant = do_QueryInterface(mTarget);
NS_ASSERTION(participant, "must implement nsIDOMGCParticipant");
ls->mListener.Set(aListener, participant);
ls->mFlags = aFlags;
ls->mSubType = aSubType;
ls->mSubTypeCapture = NS_EVENT_BITS_NONE;
ls->mHandlerIsString = 0;
ls->mGroupFlags = group;
listeners->AppendElement((void*)ls);
}
return NS_OK;
@ -809,13 +809,13 @@ nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener,
for (PRInt32 i=0; i<listeners->Count(); i++) {
ls = (nsListenerStruct*)listeners->ElementAt(i);
if (ls->mListener == aListener &&
nsRefPtr<nsIDOMEventListener> iListener = ls->mListener.Get();
if (iListener == aListener &&
(ls->mFlags & ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED) == aFlags) {
ls->mSubType &= ~aSubType;
if (ls->mSubType == NS_EVENT_BITS_NONE) {
NS_RELEASE(ls->mListener);
listeners->RemoveElement((void*)ls);
PR_DELETE(ls);
delete ls;
listenerRemoved = PR_TRUE;
}
break;
@ -1403,14 +1403,12 @@ nsEventListenerManager::RemoveScriptEventListener(nsIAtom *aName)
if (ls) {
ls->mSubType &= ~flags;
if (ls->mSubType == NS_EVENT_BITS_NONE) {
NS_RELEASE(ls->mListener);
//Get the listeners array so we can remove ourselves from it
nsVoidArray* listeners;
listeners = GetListenersByType(arrayType, nsnull, PR_FALSE);
NS_ENSURE_TRUE(listeners, NS_ERROR_FAILURE);
listeners->RemoveElement((void*)ls);
PR_DELETE(ls);
delete ls;
}
}
@ -1634,6 +1632,7 @@ nsEventListenerManager::CompileEventHandlerInternal(nsIScriptContext *aContext,
nsresult
nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
nsIDOMEventListener* aListener,
nsIDOMEvent* aDOMEvent,
nsIDOMEventTarget* aCurrentTarget,
PRUint32 aSubType,
@ -1659,7 +1658,7 @@ nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
}
if (aListenerStruct->mHandlerIsString & aSubType) {
nsCOMPtr<nsIJSEventListener> jslistener = do_QueryInterface(aListenerStruct->mListener);
nsCOMPtr<nsIJSEventListener> jslistener = do_QueryInterface(aListener);
if (jslistener) {
nsAutoString eventString;
if (NS_SUCCEEDED(aDOMEvent->GetType(eventString))) {
@ -1683,7 +1682,7 @@ nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIPrivateDOMEvent> aPrivDOMEvent(do_QueryInterface(aDOMEvent));
aPrivDOMEvent->SetCurrentTarget(aCurrentTarget);
result = aListenerStruct->mListener->HandleEvent(aDOMEvent);
result = aListener->HandleEvent(aDOMEvent);
aPrivDOMEvent->SetCurrentTarget(nsnull);
}
@ -1772,19 +1771,23 @@ nsEventListenerManager::HandleEvent(nsPresContext* aPresContext,
ls->mGroupFlags == currentGroup &&
(NS_IS_TRUSTED_EVENT(aEvent) ||
ls->mFlags & NS_PRIV_EVENT_UNTRUSTED_PERMITTED)) {
// Try the type-specific listener interface
PRBool hasInterface = PR_FALSE;
if (typeData)
DispatchToInterface(*aDOMEvent, ls->mListener,
dispData->method, *typeData->iid,
&hasInterface);
nsRefPtr<nsIDOMEventListener> eventListener = ls->mListener.Get();
NS_ASSERTION(eventListener, "listener wasn't preserved properly");
if (eventListener) {
// Try the type-specific listener interface
PRBool hasInterface = PR_FALSE;
if (typeData)
DispatchToInterface(*aDOMEvent, eventListener,
dispData->method, *typeData->iid,
&hasInterface);
// If it doesn't implement that, call the generic HandleEvent()
if (!hasInterface && (ls->mSubType == NS_EVENT_BITS_NONE ||
ls->mSubType & dispData->bits)) {
HandleEventSubType(ls, *aDOMEvent, aCurrentTarget,
dispData ? dispData->bits : NS_EVENT_BITS_NONE,
aFlags);
// If it doesn't implement that, call the generic HandleEvent()
if (!hasInterface && (ls->mSubType == NS_EVENT_BITS_NONE ||
ls->mSubType & dispData->bits)) {
HandleEventSubType(ls, eventListener, *aDOMEvent, aCurrentTarget,
dispData ? dispData->bits : NS_EVENT_BITS_NONE,
aFlags);
}
}
}
}

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

@ -45,12 +45,22 @@
#include "nsIDOM3EventTarget.h"
#include "nsHashtable.h"
#include "nsIScriptContext.h"
#include "nsJSUtils.h"
class nsIDOMEvent;
class nsIAtom;
typedef struct {
nsIDOMEventListener* mListener;
// The nsMarkedJSFunctionHolder does magic to avoid holding strong
// references to listeners implemented in JS. Instead, it protects
// them from garbage collection using nsDOMClassInfo::PreserveWrapper,
// which protects the event listener from garbage collection as long
// as it is still reachable from JS using C++ getters. (It exposes
// reachability information to the JS GC instead of treating the C++
// reachability information as own-in root-out, which creates roots
// that cause reference cycles to entrain garbage.)
nsMarkedJSFunctionHolder<nsIDOMEventListener> mListener;
PRUint16 mFlags;
PRUint16 mGroupFlags;
PRUint8 mSubType;
@ -196,6 +206,7 @@ public:
protected:
nsresult HandleEventSubType(nsListenerStruct* aListenerStruct,
nsIDOMEventListener* aListener,
nsIDOMEvent* aDOMEvent,
nsIDOMEventTarget* aCurrentTarget,
PRUint32 aSubType,

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

@ -103,7 +103,7 @@
#include "prprf.h"
nsresult NS_DOMClassInfo_PreserveWrapper(nsIXPConnectWrappedNative *aWrapper);
nsresult NS_DOMClassInfo_PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper);
// Helper classes
@ -1164,7 +1164,7 @@ nsXBLBinding::InitClass(const nsCString& aClassName,
do_QueryInterface(wrapper);
if (native_wrapper) {
NS_DOMClassInfo_PreserveWrapper(native_wrapper);
NS_DOMClassInfo_PreserveNodeWrapper(native_wrapper);
}
}

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

@ -48,7 +48,7 @@
#include "nsIXBLDocumentInfo.h"
#include "nsIDOMNode.h"
nsresult NS_DOMClassInfo_PreserveWrapper(nsIXPConnectWrappedNative *aWrapper);
nsresult NS_DOMClassInfo_PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper);
nsresult
nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aBinding, nsIContent* aBoundElement)
@ -151,7 +151,7 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding,
if (doc) {
nsCOMPtr<nsIXPConnectWrappedNative> nativeWrapper(do_QueryInterface(wrapper));
if (nativeWrapper) {
NS_DOMClassInfo_PreserveWrapper(nativeWrapper);
NS_DOMClassInfo_PreserveNodeWrapper(nativeWrapper);
}
}

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

@ -74,7 +74,9 @@ nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler()
delete mHandler;
}
NS_IMPL_ISUPPORTS1(nsXBLWindowKeyHandler, nsIDOMKeyListener)
NS_IMPL_ISUPPORTS2(nsXBLWindowKeyHandler,
nsIDOMKeyListener,
nsIDOMEventListener)
static void
BuildHandlerChain(nsIContent* aContent, nsXBLPrototypeHandler** aResult)

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

@ -41,14 +41,16 @@
#define nsPIWindowRoot_h__
#include "nsISupports.h"
#include "nsIDOMGCParticipant.h"
class nsIFocusController;
// {575CB0E1-E6C4-484a-99F8-C47B06C0E521}
// a22236a5-db06-4653-94b6-c4b6068e053c
#define NS_IWINDOWROOT_IID \
{ 0x575cb0e1, 0xe6c4, 0x484a, { 0x99, 0xf8, 0xc4, 0x7b, 0x6, 0xc0, 0xe5, 0x21 } }
{ 0xa22236a5, 0xdb06, 0x4653, \
{ 0x94, 0xb6, 0xc4, 0xb6, 0x06, 0x8e, 0x05, 0x3c } }
class nsPIWindowRoot : public nsISupports {
class nsPIWindowRoot : public nsIDOMGCParticipant {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWINDOWROOT_IID)

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

@ -196,10 +196,6 @@ enum nsDOMClassInfoID {
// DOM Traversal classes
eDOMClassInfo_TreeWalker_id,
// We are now trying to preserve binary compat in classinfo. No
// more putting things in those categories up there. New entries
// are to be added right before eDOMClassInfoIDCount.
// Rect object used by getComputedStyle
eDOMClassInfo_CSSRect_id,
@ -365,6 +361,13 @@ enum nsDOMClassInfoID {
eDOMClassInfo_XPathNSResolver_id,
eDOMClassInfo_XPathResult_id,
eDOMClassInfo_WindowRoot_id,
// We are now trying to preserve binary compat in classinfo. No more
// putting things in those categories up there. New entries are to be
// added here, which is the end of the things that are currently on by
// default.
// Define this near the end so that enabling/disabling foreignobject doesn't
// break binary compatibility
#if defined(MOZ_SVG) && defined(MOZ_SVG_FOREIGNOBJECT)

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

@ -420,6 +420,7 @@ static const char kDOMStringBundleURL[] =
nsIXPCScriptable::WANT_ADDPROPERTY | \
nsIXPCScriptable::WANT_DELPROPERTY | \
nsIXPCScriptable::WANT_NEWENUMERATE | \
nsIXPCScriptable::WANT_MARK | \
nsIXPCScriptable::WANT_EQUALITY | \
nsIXPCScriptable::WANT_OUTER_OBJECT | \
nsIXPCScriptable::WANT_INNER_OBJECT | \
@ -1081,6 +1082,12 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(XPathResult, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
// We just want this to have classinfo so it gets mark callbacks for marking
// event listeners.
// We really don't want any of the default flags!
NS_DEFINE_CLASSINFO_DATA(WindowRoot, nsEventReceiverSH,
nsIXPCScriptable::WANT_MARK)
// Define MOZ_SVG_FOREIGNOBJECT here so that when it gets switched on,
// we preserve binary compatibility. New classes should be added
// at the end.
@ -2952,6 +2959,11 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathResult)
DOM_CLASSINFO_MAP_END
// We just want this to have classinfo so it gets mark callbacks for marking
// event listeners.
DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(WindowRoot, nsISupports)
DOM_CLASSINFO_MAP_END
#if defined(MOZ_SVG) && defined(MOZ_SVG_FOREIGNOBJECT)
DOM_CLASSINFO_MAP_BEGIN(SVGForeignObjectElement, nsIDOMSVGForeignObjectElement)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGForeignObjectElement)
@ -4865,105 +4877,23 @@ nsDOMClassInfo::InitDOMJSClass(JSContext *cx, JSObject *obj)
#endif
/**
* Every XPConnect wrapper that needs to be preserved (because it has JS
* properties set on it) as long as the element it wraps is reachable
* from script (via JS or via DOM APIs accessible from JS) gets an entry
* in this table.
* Every XPConnect wrapper that needs to be preserved (a wrapped native
* with JS properties set on it or used by XBL or a wrapped JS event
* handler function) as long as the element it wraps is reachable from
* script (via JS or via DOM APIs accessible from JS) gets an entry in
* this table.
*/
static PLDHashTable sPreservedWrapperTable;
struct PreservedWrapperEntry : public PLDHashEntryHdr {
nsIDOMNode* key; // must be first to line up with PLDHashEntryStub
nsIXPConnectWrappedNative *wrapper;
void *key; // must be first to line up with PLDHashEntryStub
nsIXPConnectJSObjectHolder* (*keyToWrapperFunc)(void* aKey);
nsIDOMGCParticipant *participant;
// See |WrapperSCCEntry::first|. Valid only during mark phase of GC.
PreservedWrapperEntry *next;
};
/**
* During the Mark phase of the GC, we need to mark all of the preserved
* wrappers that are reachable via DOM APIs. Since reachability for DOM
* nodes is symmetric, if one DOM node is reachable from another via DOM
* APIs, then they are in the same strongly connected component.
* (Strongly connected components are never reachable from each other
* via DOM APIs.) We can refer to each strongly connected component by
* walking up to the top of the parent chain. This function finds that
* root node for any DOM node.
*/
static nsIDOMNode *
GetSCCRootFor(nsIDOMNode *aDOMNode)
{
nsCOMPtr<nsIDOMNode> cur(aDOMNode), next;
#ifdef DEBUG_NOISY_PRESERVE_WRAPPERS
nsCOMArray<nsIDOMNode> stack;
#endif
PRUint16 nodeType;
cur->GetNodeType(&nodeType);
if (nodeType == nsIDOMNode::ATTRIBUTE_NODE) {
nsCOMPtr<nsIDOMAttr> attr = do_QueryInterface(cur);
nsCOMPtr<nsIDOMElement> owner;
attr->GetOwnerElement(getter_AddRefs(owner));
if (owner) {
#ifdef DEBUG_NOISY_PRESERVE_WRAPPERS
stack.AppendObject(cur);
#endif
cur = do_QueryInterface(owner);
}
}
for (;;) {
#ifdef DEBUG_NOISY_PRESERVE_WRAPPERS
stack.AppendObject(cur);
#endif
cur->GetParentNode(getter_AddRefs(next));
if (!next) {
#ifdef DEBUG_NOISY_PRESERVE_WRAPPERS
PRUint16 nodeType;
cur->GetNodeType(&nodeType);
if (nodeType != nsIDOMNode::DOCUMENT_NODE) {
printf(" non-document root:");
nsAutoString nodeName;
for (PRInt32 i = stack.Count() - 1; i >= 0; --i) {
stack[i]->GetNodeName(nodeName);
stack[i]->GetNodeType(&nodeType);
if (nodeType == nsIDOMNode::ATTRIBUTE_NODE) {
nsAutoString nodeValue;
stack[i]->GetNodeValue(nodeValue);
printf(" > @%s=\"%s\"", NS_ConvertUTF16toUTF8(nodeName).get(),
NS_ConvertUTF16toUTF8(nodeValue).get());
} else {
printf(" > %s", NS_ConvertUTF16toUTF8(nodeName).get());
if (nodeType == nsIDOMNode::ELEMENT_NODE) {
nsCOMPtr<nsIContent> content = do_QueryInterface(stack[i]);
PRInt32 j;
for (j = 0, j_end = content->GetAttrCount(); j < j_end; ++j) {
PRInt32 namespaceID;
nsCOMPtr<nsIAtom> name, prefix;
content->GetAttrNameAt(j, &namespaceID, getter_AddRefs(name),
getter_AddRefs(prefix));
nsAutoString val;
nsCAutoString atomStr;
content->GetAttr(namespaceID, name, val);
printf("[");
if (prefix) {
prefix->ToUTF8String(atomStr);
printf("%s:", atomStr.get());
}
name->ToUTF8String(atomStr);
printf("%s=\"%s\"]", atomStr.get(),
NS_ConvertUTF16toUTF8(val).get());
}
}
}
}
printf("\n");
}
#endif
return cur;
}
next.swap(cur);
}
}
/**
* At the beginning of the mark phase of the GC, we sort all the
* wrappers into their strongly connected components. We maintain this
@ -4985,7 +4915,7 @@ static PLDHashTable sWrapperSCCTable;
struct WrapperSCCEntry : public PLDHashEntryHdr {
// This could probably be a weak pointer (which would avoid the
// need for hash table ops), but it seems safer this way.
nsCOMPtr<nsIDOMNode> key; // must be first to line up with PLDHashEntryStub
nsCOMPtr<nsIDOMGCParticipant> key; // must be first to line up with PLDHashEntryStub
// Linked list of preserved wrappers in the strongly connected
// component, to be traversed using |PreservedWrapperEntry::next|.
@ -4993,7 +4923,7 @@ struct WrapperSCCEntry : public PLDHashEntryHdr {
PRBool marked;
WrapperSCCEntry(nsIDOMNode *aKey)
WrapperSCCEntry(nsIDOMGCParticipant *aKey)
: key(aKey), first(nsnull), marked(PR_FALSE) {}
};
@ -5010,8 +4940,8 @@ WrapperSCCsInitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
const void *key)
{
WrapperSCCEntry *entry = NS_STATIC_CAST(WrapperSCCEntry*, hdr);
new (entry) WrapperSCCEntry(NS_STATIC_CAST(nsIDOMNode*,
NS_CONST_CAST(void*, key)));
new (entry) WrapperSCCEntry(NS_STATIC_CAST(nsIDOMGCParticipant*,
NS_CONST_CAST(void*, key)));
return PR_TRUE;
}
@ -5030,14 +4960,16 @@ static const PLDHashTableOps sWrapperSCCTableOps = {
// static
nsresult
nsDOMClassInfo::PreserveWrapper(nsIXPConnectWrappedNative *aWrapper)
nsDOMClassInfo::PreserveWrapper(void *aKey,
nsIXPConnectJSObjectHolder* (*aKeyToWrapperFunc)(void* aKey),
nsIDOMGCParticipant *aParticipant)
{
nsCOMPtr<nsIDOMNode> node = do_QueryWrappedNative(aWrapper);
if (!node) {
return NS_OK;
}
nsIDOMNode* nodePtr = node;
NS_PRECONDITION(aKey, "unexpected null pointer");
NS_PRECONDITION(aKeyToWrapperFunc, "unexpected null pointer");
NS_PRECONDITION(aParticipant, "unexpected null pointer");
NS_ASSERTION(!sWrapperSCCTable.ops,
"cannot change preserved wrapper table during mark phase");
if (!sPreservedWrapperTable.ops &&
!PL_DHashTableInit(&sPreservedWrapperTable, PL_DHashGetStubOps(), nsnull,
sizeof(PreservedWrapperEntry), 16)) {
@ -5046,22 +4978,43 @@ nsDOMClassInfo::PreserveWrapper(nsIXPConnectWrappedNative *aWrapper)
}
PreservedWrapperEntry *entry = NS_STATIC_CAST(PreservedWrapperEntry*,
PL_DHashTableOperate(&sPreservedWrapperTable, nodePtr, PL_DHASH_ADD));
PL_DHashTableOperate(&sPreservedWrapperTable, aKey, PL_DHASH_ADD));
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
entry->key = nodePtr;
entry->wrapper = aWrapper;
entry->key = aKey;
entry->keyToWrapperFunc = aKeyToWrapperFunc;
entry->participant = aParticipant;
return NS_OK;
}
static nsIXPConnectJSObjectHolder* IdentityKeyToWrapperFunc(void* aKey)
{
return NS_STATIC_CAST(nsIXPConnectJSObjectHolder*, aKey);
}
// static
nsresult
nsDOMClassInfo::PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper)
{
nsCOMPtr<nsIDOMGCParticipant> participant =
do_QueryInterface(aWrapper->Native());
return nsDOMClassInfo::PreserveWrapper(aWrapper, IdentityKeyToWrapperFunc,
participant);
}
// static
void
nsDOMClassInfo::ReleaseWrapper(nsIDOMNode *aDOMNode)
nsDOMClassInfo::ReleaseWrapper(void *aKey)
{
NS_PRECONDITION(aKey, "unexpected null pointer");
NS_ASSERTION(!sWrapperSCCTable.ops,
"cannot change preserved wrapper table during mark phase");
if (sPreservedWrapperTable.ops) {
PL_DHashTableOperate(&sPreservedWrapperTable, aDOMNode, PL_DHASH_REMOVE);
PL_DHashTableOperate(&sPreservedWrapperTable, aKey, PL_DHASH_REMOVE);
if (sPreservedWrapperTable.entryCount == 0) {
PL_DHashTableFinish(&sPreservedWrapperTable);
sPreservedWrapperTable.ops = nsnull;
@ -5081,8 +5034,10 @@ MarkAllWrappers(PLDHashTable *table, PLDHashEntryHdr *hdr,
MarkAllWrappersData *data = NS_STATIC_CAST(MarkAllWrappersData*, arg);
PreservedWrapperEntry *entry = NS_STATIC_CAST(PreservedWrapperEntry*, hdr);
nsIXPConnectJSObjectHolder *wrapper;
JSObject *wrapper_obj;
if (NS_SUCCEEDED(entry->wrapper->GetJSObject(&wrapper_obj)))
if ((wrapper = entry->keyToWrapperFunc(entry->key)) &&
NS_SUCCEEDED(wrapper->GetJSObject(&wrapper_obj)))
JS_MarkGCThing(data->cx, wrapper_obj,
"nsDOMClassInfo::sPreservedWrapperTable_OOM", data->arg);
@ -5091,7 +5046,7 @@ MarkAllWrappers(PLDHashTable *table, PLDHashEntryHdr *hdr,
// static
void
nsDOMClassInfo::MarkReachablePreservedWrappers(nsIDOMNode *aDOMNode,
nsDOMClassInfo::MarkReachablePreservedWrappers(nsIDOMGCParticipant *aParticipant,
JSContext *cx, void *arg)
{
// Magic value indicating we've hit out-of-memory earlier in this GC.
@ -5118,25 +5073,40 @@ nsDOMClassInfo::MarkReachablePreservedWrappers(nsIDOMNode *aDOMNode,
return;
}
nsIDOMGCParticipant *SCCIndex = aParticipant->GetSCCIndex();
WrapperSCCEntry *entry = NS_STATIC_CAST(WrapperSCCEntry*,
PL_DHashTableOperate(&sWrapperSCCTable, GetSCCRootFor(aDOMNode),
PL_DHASH_LOOKUP));
PL_DHashTableOperate(&sWrapperSCCTable, SCCIndex, PL_DHASH_LOOKUP));
if (!PL_DHASH_ENTRY_IS_BUSY(entry) || entry->marked)
return;
#ifdef DEBUG_PRESERVE_WRAPPERS
{
nsAutoString nodeName;
entry->key->GetNodeName(nodeName);
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(entry->key);
if (node)
node->GetNodeName(nodeName);
else
nodeName.AssignLiteral("##not-node##");
printf(" marking entries for SCC root %p \"%s\"\n",
entry->key.get(), NS_ConvertUTF16toUTF8(nodeName).get());
NS_STATIC_CAST(void*, entry->key.get()),
NS_ConvertUTF16toUTF8(nodeName).get());
}
#endif
entry->marked = PR_TRUE;
// Do the reachable list first to encourage shorter call stacks
// (perhaps slightly less recursion through JS marking).
nsCOMArray<nsIDOMGCParticipant> reachable;
SCCIndex->AppendReachableList(reachable);
for (PRInt32 i = 0, i_end = reachable.Count(); i < i_end; ++i) {
MarkReachablePreservedWrappers(reachable[i], cx, arg);
}
for (PreservedWrapperEntry *pwe = entry->first; pwe; pwe = pwe->next) {
nsIXPConnectJSObjectHolder *wrapper;
JSObject *wrapper_obj;
if (NS_SUCCEEDED(pwe->wrapper->GetJSObject(&wrapper_obj)))
if ((wrapper = pwe->keyToWrapperFunc(pwe->key)) &&
NS_SUCCEEDED(wrapper->GetJSObject(&wrapper_obj)))
::JS_MarkGCThing(cx, wrapper_obj,
"nsDOMClassInfo::sPreservedWrapperTable", arg);
}
@ -5149,7 +5119,7 @@ ClassifyWrapper(PLDHashTable *table, PLDHashEntryHdr *hdr,
PreservedWrapperEntry *entry = NS_STATIC_CAST(PreservedWrapperEntry*, hdr);
WrapperSCCEntry *SCCEntry = NS_STATIC_CAST(WrapperSCCEntry*,
PL_DHashTableOperate(&sWrapperSCCTable, GetSCCRootFor(entry->key),
PL_DHashTableOperate(&sWrapperSCCTable, entry->participant->GetSCCIndex(),
PL_DHASH_ADD));
if (!SCCEntry) {
*NS_STATIC_CAST(PRBool*, arg) = PR_TRUE;
@ -5159,9 +5129,14 @@ ClassifyWrapper(PLDHashTable *table, PLDHashEntryHdr *hdr,
#ifdef DEBUG_PRESERVE_WRAPPERS
if (!SCCEntry->first) {
nsAutoString nodeName;
SCCEntry->key->GetNodeName(nodeName);
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(SCCEntry->key);
if (node)
node->GetNodeName(nodeName);
else
nodeName.AssignLiteral("##not-node##");
printf(" new SCC root %p \"%s\"\n",
SCCEntry->key.get(), NS_ConvertUTF16toUTF8(nodeName).get());
NS_STATIC_CAST(void*, SCCEntry->key.get()),
NS_ConvertUTF16toUTF8(nodeName).get());
}
#endif
@ -5178,7 +5153,7 @@ nsDOMClassInfo::BeginGCMark()
NS_PRECONDITION(!sWrapperSCCTable.ops, "table already initialized");
#ifdef DEBUG_PRESERVE_WRAPPERS
printf("\nClassifying preserved wrappers into SCCs:\n");
printf("Classifying preserved wrappers into SCCs:\n");
#endif
if (!PL_DHashTableInit(&sWrapperSCCTable, &sWrapperSCCTableOps, nsnull,
@ -5213,11 +5188,11 @@ nsDOMClassInfo::EndGCMark()
}
}
// hack to give XBL access to nsDOMClassInfo::PreserveWrapper
// hack to give XBL access to nsDOMClassInfo::PreserveNodeWrapper
nsresult
NS_DOMClassInfo_PreserveWrapper(nsIXPConnectWrappedNative *aWrapper)
NS_DOMClassInfo_PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper)
{
return nsDOMClassInfo::PreserveWrapper(aWrapper);
return nsDOMClassInfo::PreserveNodeWrapper(aWrapper);
}
// static
@ -6007,7 +5982,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
if (id == sDocument_id) {
nsCOMPtr<nsIDOMDocument> document;
nsresult rv = win->GetDocument(getter_AddRefs(document));
rv = win->GetDocument(getter_AddRefs(document));
NS_ENSURE_SUCCESS(rv, rv);
jsval v;
@ -6151,7 +6126,7 @@ nsWindowSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
sgo->OnFinalize(obj);
return NS_OK;
return nsEventReceiverSH::Finalize(wrapper, cx, obj);
}
NS_IMETHODIMP
@ -6431,37 +6406,12 @@ nsNodeSH::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
{
// This can fail on out-of-memory, which should end up throwing a JS
// exception.
nsresult rv = nsDOMClassInfo::PreserveWrapper(wrapper);
nsresult rv = nsDOMClassInfo::PreserveNodeWrapper(wrapper);
NS_ENSURE_SUCCESS(rv, rv);
return nsEventReceiverSH::AddProperty(wrapper, cx, obj, id, vp, _retval);
}
NS_IMETHODIMP
nsNodeSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj)
{
nsISupports *native = wrapper->Native();
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(native));
nsDOMClassInfo::ReleaseWrapper(node);
return NS_OK;
}
NS_IMETHODIMP
nsNodeSH::Mark(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, void *arg, PRUint32 *_retval)
{
nsISupports *native = wrapper->Native();
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(native));
nsDOMClassInfo::MarkReachablePreservedWrappers(node, cx, arg);
*_retval = 1;
return NS_OK;
}
NS_IMETHODIMP
nsNodeSH::GetFlags(PRUint32 *aFlags)
{
@ -6587,7 +6537,6 @@ nsEventReceiverSH::AddEventListenerHelper(JSContext *cx, JSObject *obj,
return JS_FALSE;
}
nsresult rv;
nsCOMPtr<nsIDOMNSEventTarget> eventTarget(do_QueryWrappedNative(wrapper,
&rv));
if (NS_FAILED(rv)) {
@ -6604,7 +6553,6 @@ nsEventReceiverSH::AddEventListenerHelper(JSContext *cx, JSObject *obj,
return JS_FALSE;
}
} else {
nsresult rv;
nsCOMPtr<nsIDOMEventTarget> eventTarget(do_QueryWrappedNative(wrapper,
&rv));
if (NS_FAILED(rv)) {
@ -6732,13 +6680,27 @@ nsEventReceiverSH::AddProperty(nsIXPConnectWrappedNative *wrapper,
return nsEventReceiverSH::SetProperty(wrapper, cx, obj, id, vp, _retval);
}
/*
NS_IMETHODIMP
nsEventReceiverSH::OnFinalize(...)
nsEventReceiverSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj)
{
clear event handlers in mListener...
// XXX clear event handlers in mListener...
nsDOMClassInfo::ReleaseWrapper(wrapper);
return NS_OK;
}
NS_IMETHODIMP
nsEventReceiverSH::Mark(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, void *arg, PRUint32 *_retval)
{
nsCOMPtr<nsIDOMGCParticipant> participant(do_QueryWrappedNative(wrapper));
nsDOMClassInfo::MarkReachablePreservedWrappers(participant, cx, arg);
*_retval = 1;
return NS_OK;
}
*/
// Element helper
@ -7411,8 +7373,8 @@ nsDocumentSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
jsval winVal;
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
nsresult rv = WrapNative(cx, obj, win, NS_GET_IID(nsIDOMWindow), &winVal,
getter_AddRefs(holder));
rv = WrapNative(cx, obj, win, NS_GET_IID(nsIDOMWindow), &winVal,
getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
NS_NAMED_LITERAL_STRING(doc_str, "document");

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

@ -53,6 +53,7 @@ class nsIForm;
class nsIDOMNode;
class nsIDOMNodeList;
class nsIDOMDocument;
class nsIDOMGCParticipant;
class nsIHTMLDocument;
class nsGlobalWindow;
@ -174,22 +175,34 @@ public:
}
/**
* Note that the XPConnect wrapper should be preserved. This will only
* preserve aWrapper if its native QIs to nsIDOMNode; otherwise it'll just
* return NS_OK.
* Note that the XPConnect wrapper should be protected from garbage
* collection as long as the GC participant is reachable.
*
* A preservation with a given key overwrites any previous
* preservation with that key.
*/
static nsresult PreserveWrapper(nsIXPConnectWrappedNative *aWrapper);
static nsresult PreserveWrapper(void* aKey,
nsIXPConnectJSObjectHolder* (*aKeyToWrapperFunc)(void* aKey),
nsIDOMGCParticipant *aParticipant);
/**
* Undoes the effects of any prior |PreserveWrapper| calls on
* |aDOMNode|.
* Easier way to call the above just for DOM nodes (and better, since
* we get the performance benefits of having the same identity function).
* The call to |PreserveWrapper| is made with |aKey| == |aWrapper|.
*/
static void ReleaseWrapper(nsIDOMNode *aDOMNode);
static nsresult PreserveNodeWrapper(nsIXPConnectWrappedNative *aWrapper);
/**
* Undoes the effects of any prior |PreserveWrapper| calls made with
* |aKey|.
*/
static void ReleaseWrapper(void* aKey);
/**
* Mark all preserved wrappers reachable from |aDOMNode| via DOM APIs.
*/
static void MarkReachablePreservedWrappers(nsIDOMNode *aDOMNode,
static void MarkReachablePreservedWrappers(nsIDOMGCParticipant *aParticipant,
JSContext *cx, void *arg);
/**
@ -401,6 +414,10 @@ public:
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);
NS_IMETHOD Mark(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, void *arg, PRUint32 *_retval);
};
@ -541,10 +558,6 @@ public:
JSObject *globalObj, JSObject **parentObj);
NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, jsval id, jsval *vp, PRBool *_retval);
NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj);
NS_IMETHOD Mark(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj, void *arg, PRUint32 *_retval);
NS_IMETHOD GetFlags(PRUint32 *aFlags);
static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)

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

@ -488,6 +488,7 @@ NS_INTERFACE_MAP_BEGIN(nsGlobalWindow)
NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject)
NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventReceiver)
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
NS_INTERFACE_MAP_ENTRY(nsIDOMNSEventTarget)
@ -5163,6 +5164,8 @@ nsGlobalWindow::GetListenerManager(nsIEventListenerManager **aResult)
mListenerManager = do_CreateInstance(kEventListenerManagerCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
mListenerManager->SetListenerTarget(
NS_STATIC_CAST(nsIDOMEventReceiver*, this));
}
NS_ADDREF(*aResult = mListenerManager);
@ -5187,6 +5190,37 @@ nsGlobalWindow::GetSystemEventGroup(nsIDOMEventGroup **aGroup)
return NS_ERROR_FAILURE;
}
//*****************************************************************************
// nsGlobalWindow::nsIDOMGCParticipant
//*****************************************************************************
nsIDOMGCParticipant*
nsGlobalWindow::GetSCCIndex()
{
return this;
}
static void AppendToReachableList(nsISupports *aObject,
nsCOMArray<nsIDOMGCParticipant>& aArray)
{
nsCOMPtr<nsIDOMGCParticipant> p = do_QueryInterface(aObject);
if (p)
aArray.AppendObject(p);
}
void
nsGlobalWindow::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
{
AppendToReachableList(mChromeEventHandler, aArray);
AppendToReachableList(mDocument, aArray);
// XXXldb Do we want this to go both ways?
if (IsOuterWindow()) {
AppendToReachableList(mInnerWindow, aArray);
} else {
AppendToReachableList(mOuterWindow, aArray);
}
}
//*****************************************************************************
// nsGlobalWindow::nsPIDOMWindow
//*****************************************************************************

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

@ -89,6 +89,7 @@
#include "nsSize.h"
#include "mozFlushType.h"
#include "prclist.h"
#include "nsIDOMGCParticipant.h"
#define DEFAULT_HOME_PAGE "www.mozilla.org"
#define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage"
@ -134,6 +135,7 @@ class nsGlobalWindow : public nsPIDOMWindow,
public nsIDOMJSWindow,
public nsIScriptObjectPrincipal,
public nsIDOMEventReceiver,
public nsIDOMGCParticipant,
public nsIDOM3EventTarget,
public nsIDOMNSEventTarget,
public nsIDOMViewCSS,
@ -193,6 +195,10 @@ public:
NS_IMETHOD HandleEvent(nsIDOMEvent *aEvent);
NS_IMETHOD GetSystemEventGroup(nsIDOMEventGroup** aGroup);
// nsIDOMGCParticipant
virtual nsIDOMGCParticipant* GetSCCIndex();
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
// nsPIDOMWindow
virtual NS_HIDDEN_(nsPIDOMWindow*) GetPrivateRoot();
virtual NS_HIDDEN_(nsresult) GetObjectProperty(const PRUnichar* aProperty,

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

@ -78,6 +78,7 @@
#include "nsIAtom.h"
#include "nsContentUtils.h"
#include "jscntxt.h"
#include "nsIDOMGCParticipant.h"
// For locale aware string methods
#include "plstr.h"
@ -87,6 +88,7 @@
#include "nsILocaleService.h"
#include "nsICollation.h"
#include "nsCollationCID.h"
#include "nsDOMClassInfo.h"
#ifdef NS_DEBUG
#include "jsgc.h" // for WAY_TOO_MUCH_GC, if defined for GC debugging
@ -2145,12 +2147,10 @@ nsJSContext::ScriptExecuted()
return NS_OK;
}
nsresult NS_DOMClassInfo_PreserveWrapper(nsIXPConnectWrappedNative *aWrapper);
NS_IMETHODIMP
nsJSContext::PreserveWrapper(nsIXPConnectWrappedNative *aWrapper)
{
return NS_DOMClassInfo_PreserveWrapper(aWrapper);
return nsDOMClassInfo::PreserveNodeWrapper(aWrapper);
}
NS_IMETHODIMP

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

@ -21,6 +21,7 @@
*
* Contributor(s):
* Vidur Apparao <vidur@netscape.com>
* L. David Baron <dbaron@mozillafoundation.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -54,6 +55,9 @@
#include "nsIXPConnect.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsDOMClassInfo.h"
#include "nsIDOMGCParticipant.h"
#include "nsIWeakReference.h"
JSBool
@ -212,3 +216,94 @@ nsJSUtils::GetDynamicScriptContext(JSContext *aContext)
return GetScriptContextFromJSContext(aContext);
}
#define MARKED_OBJECT_BIT (PRWord(1<<0))
void
nsMarkedJSFunctionHolder_base::Set(nsISupports *aPotentialFunction,
nsIDOMGCParticipant *aParticipant)
{
if (PRWord(mObject) & MARKED_OBJECT_BIT) {
nsDOMClassInfo::ReleaseWrapper(this);
}
nsISupports *oldVal = (nsISupports*)(PRWord(mObject) & ~MARKED_OBJECT_BIT);
if (!TryMarkedSet(aPotentialFunction, aParticipant)) {
NS_ASSERTION((PRWord(aPotentialFunction) & MARKED_OBJECT_BIT) == 0,
"low bit set");
NS_IF_ADDREF(aPotentialFunction);
mObject = aPotentialFunction;
}
NS_IF_RELEASE(oldVal);
}
static nsIXPConnectJSObjectHolder* HolderToWrappedJS(void *aKey)
{
nsMarkedJSFunctionHolder_base *holder = NS_STATIC_CAST(
nsMarkedJSFunctionHolder_base*, aKey);
NS_ASSERTION(PRWord(holder->mObject) & MARKED_OBJECT_BIT,
"yikes, not a marked object");
nsIWeakReference* weakRef =
(nsIWeakReference*)(PRWord(holder->mObject) & ~MARKED_OBJECT_BIT);
// This entire interface is a hack to avoid reference counting, so
// this actually doesn't do any reference counting, and we don't leak
// anything. This is needed so we don't add and remove GC roots in
// the middle of GC.
nsWeakRefToIXPConnectWrappedJS *result;
if (NS_FAILED(CallQueryReferent(weakRef, &result)))
result = nsnull;
return result;
}
PRBool
nsMarkedJSFunctionHolder_base::TryMarkedSet(nsISupports *aPotentialFunction,
nsIDOMGCParticipant *aParticipant)
{
NS_ENSURE_TRUE(aParticipant, PR_FALSE);
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS =
do_QueryInterface(aPotentialFunction);
if (!wrappedJS) // a non-JS implementation
return PR_FALSE;
nsresult rv =
nsDOMClassInfo::PreserveWrapper(this, HolderToWrappedJS, aParticipant);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
nsIWeakReference* weakRef; // [STRONG]
wrappedJS->GetWeakReference(&weakRef);
NS_ENSURE_TRUE(weakRef, PR_FALSE);
NS_ASSERTION((PRWord(weakRef) & MARKED_OBJECT_BIT) == 0, "low bit set");
mObject = (nsISupports*)(PRWord(weakRef) | MARKED_OBJECT_BIT);
return PR_TRUE;
}
already_AddRefed<nsISupports>
nsMarkedJSFunctionHolder_base::Get(REFNSIID aIID)
{
nsISupports *result;
if (PRWord(mObject) & MARKED_OBJECT_BIT) {
nsIWeakReference* weakRef =
(nsIWeakReference*)(PRWord(mObject) & ~MARKED_OBJECT_BIT);
nsresult rv =
weakRef->QueryReferent(aIID, NS_REINTERPRET_CAST(void**, &result));
if (NS_FAILED(rv)) {
NS_NOTREACHED("GC preservation didn't work");
result = nsnull;
}
} else {
NS_IF_ADDREF(result = mObject);
}
return result;
}
nsMarkedJSFunctionHolder_base::~nsMarkedJSFunctionHolder_base()
{
if (PRWord(mObject) & MARKED_OBJECT_BIT) {
nsDOMClassInfo::ReleaseWrapper(this);
}
nsISupports *obj = (nsISupports*)(PRWord(mObject) & ~MARKED_OBJECT_BIT);
NS_IF_RELEASE(obj);
}

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

@ -48,10 +48,13 @@
#include "nsISupports.h"
#include "jsapi.h"
#include "nsString.h"
#include "nsCOMPtr.h"
class nsIDOMEventListener;
class nsIScriptContext;
class nsIScriptGlobalObject;
class nsIDOMGCParticipant;
class nsIXPConnectJSObjectHolder;
class nsJSUtils
{
@ -102,4 +105,44 @@ public:
}
};
/**
* nsMarkedJSFunctionHolder<T> is used to store objects of XPCOM
* interface T.
*
* If the object stored is an XPConnect wrapped JS object and the
* wrapper can be preserved through nsDOMClassInfo, the holder will hold
* a weak reference and preserve the object from garbage collection as
* long as the garbage collector can reach |aParticipant|; once both
* |aParticipant| and the object are unreachable it will be garbage
* collected and the holder will hold null.
*
* Otherwise, it holds a strong reference.
*/
class nsMarkedJSFunctionHolder_base
{
public:
void Set(nsISupports *aPotentialFunction, nsIDOMGCParticipant *aParticipant);
already_AddRefed<nsISupports> Get(REFNSIID aIID);
nsMarkedJSFunctionHolder_base() : mObject(nsnull) {}
~nsMarkedJSFunctionHolder_base();
PRBool TryMarkedSet(nsISupports *aPotentialFunction, nsIDOMGCParticipant *aParticipant);
nsISupports *mObject;
};
template <class T>
class nsMarkedJSFunctionHolder : protected nsMarkedJSFunctionHolder_base
{
public:
void Set(T *aPotentialFunction, nsIDOMGCParticipant *aParticipant) {
nsMarkedJSFunctionHolder_base::Set(aPotentialFunction, aParticipant);
}
already_AddRefed<T> Get() {
return already_AddRefed<T>(NS_STATIC_CAST(T*, nsMarkedJSFunctionHolder_base::Get(NS_GET_IID(T)).get()));
}
};
#endif /* nsJSUtils_h__ */

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

@ -52,6 +52,7 @@
#include "nsIDOMWindowInternal.h"
#include "nsFocusController.h"
#include "nsString.h"
#include "nsDOMClassInfo.h"
static NS_DEFINE_CID(kEventListenerManagerCID, NS_EVENTLISTENERMANAGER_CID);
@ -63,21 +64,30 @@ nsWindowRoot::nsWindowRoot(nsIDOMWindow* aWindow)
nsFocusController::Create(getter_AddRefs(mFocusController));
nsCOMPtr<nsIDOMFocusListener> focusListener(do_QueryInterface(mFocusController));
++mRefCnt;
AddEventListener(NS_LITERAL_STRING("focus"), focusListener, PR_TRUE);
AddEventListener(NS_LITERAL_STRING("blur"), focusListener, PR_TRUE);
--mRefCnt;
}
nsWindowRoot::~nsWindowRoot()
{
}
NS_IMPL_ISUPPORTS6(nsWindowRoot,
nsIDOMEventReceiver,
nsIChromeEventHandler,
nsPIWindowRoot,
nsIDOMEventTarget,
nsIDOM3EventTarget,
nsIDOMNSEventTarget)
NS_INTERFACE_MAP_BEGIN(nsWindowRoot)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventReceiver)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventReceiver)
NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
NS_INTERFACE_MAP_ENTRY(nsIChromeEventHandler)
NS_INTERFACE_MAP_ENTRY(nsPIWindowRoot)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
NS_INTERFACE_MAP_ENTRY(nsIDOMNSEventTarget)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(WindowRoot) // XXX right name?
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(nsWindowRoot)
NS_IMPL_RELEASE(nsWindowRoot)
NS_IMETHODIMP
nsWindowRoot::AddEventListener(const nsAString& aType, nsIDOMEventListener* aListener, PRBool aUseCapture)
@ -202,6 +212,8 @@ nsWindowRoot::GetListenerManager(nsIEventListenerManager** aResult)
nsresult rv;
mListenerManager = do_CreateInstance(kEventListenerManagerCID, &rv);
if (NS_FAILED(rv)) return rv;
mListenerManager->SetListenerTarget(
NS_STATIC_CAST(nsIDOMEventReceiver*, this));
}
*aResult = mListenerManager;
@ -283,6 +295,17 @@ nsWindowRoot::HandleChromeEvent(nsPresContext* aPresContext, nsEvent* aEvent,
return ret;
}
nsIDOMGCParticipant*
nsWindowRoot::GetSCCIndex()
{
return this;
}
void
nsWindowRoot::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
{
}
NS_IMETHODIMP
nsWindowRoot::GetFocusController(nsIFocusController** aResult)
{

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

@ -80,6 +80,10 @@ public:
NS_IMETHOD HandleEvent(nsIDOMEvent *aEvent);
NS_IMETHOD GetSystemEventGroup(nsIDOMEventGroup** aGroup);
// nsIDOMGCParticipant
virtual nsIDOMGCParticipant* GetSCCIndex();
virtual void AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray);
// nsPIWindowRoot
NS_IMETHOD GetFocusController(nsIFocusController** aResult);

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

@ -50,6 +50,7 @@
#include "nsIInterfaceInfoManager.idl"
#include "nsIExceptionService.idl"
#include "nsIVariant.idl"
#include "nsIWeakReference.idl"
%{ C++
#include "jspubtd.h"
@ -232,7 +233,7 @@ do_QueryWrappedNative(nsIXPConnectWrappedNative *aWrappedNative,
%}
[uuid(BED52030-BCA6-11d2-BA79-00805F8A5DD7)]
[uuid(a052e197-7ba4-494e-b735-8786ac091164)]
interface nsIXPConnectWrappedJS : nsIXPConnectJSObjectHolder
{
/* attribute 'JSObject' inherited from nsIXPConnectJSObjectHolder */
@ -243,6 +244,27 @@ interface nsIXPConnectWrappedJS : nsIXPConnectJSObjectHolder
void aggregatedQueryInterface(in nsIIDRef uuid,
[iid_is(uuid),retval] out nsQIResult result);
/* This method has the same signature and the same semantics as the
* one method on nsISupportsWeakReference. However, it exists here
* so that callers who need to manage JS garbage collection for
* wrapped objects themselves can get a weak reference to the
* wrapped JS object: in other words, it's for callers who know
* that they're dealing with a wrapper, and want a weak reference to
* the wrapper rather than the wrapped object.
*/
nsIWeakReference GetWeakReference();
};
/**
* This interface is a complete hack. It is used by the DOM code to
* call QueryReferent on a weak reference to a wrapped JS object without
* causing reference counting, which would add and remove GC roots
* (which can't be done in the middle of GC).
*/
[uuid(3f32871c-d014-4f91-b358-3ece74cbebaa)]
interface nsWeakRefToIXPConnectWrappedJS : nsIXPConnectWrappedJS
{
};
/***************************************************************************/

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

@ -2201,7 +2201,7 @@ private:
// interface on the single underlying (possibly aggregate) JSObject.
class nsXPCWrappedJS : public nsXPTCStubBase,
public nsIXPConnectWrappedJS,
public nsWeakRefToIXPConnectWrappedJS,
public nsSupportsWeakReference,
public nsIPropertyBag
{
@ -2209,7 +2209,7 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
NS_DECL_NSIXPCONNECTWRAPPEDJS
NS_DECL_NSISUPPORTSWEAKREFERENCE
//NS_DECL_NSISUPPORTSWEAKREFERENCE // methods also on nsIXPConnectWrappedJS
NS_DECL_NSIPROPERTYBAG
// Note that both nsXPTCStubBase and nsIXPConnectWrappedJS declare

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

@ -88,6 +88,13 @@ nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
return NS_OK;
}
// This interface is a hack that says "don't AddRef me".
if(aIID.Equals(NS_GET_IID(nsWeakRefToIXPConnectWrappedJS)))
{
*aInstancePtr = NS_STATIC_CAST(nsWeakRefToIXPConnectWrappedJS*, this);
return NS_OK;
}
nsISupports* outer = GetAggregatedNativeObject();
if(outer)
return outer->QueryInterface(aIID, aInstancePtr);
@ -189,10 +196,7 @@ do_decrement:
NS_IMETHODIMP
nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr)
{
if(mRoot != this)
return mRoot->GetWeakReference(aInstancePtr);
return nsSupportsWeakReference::GetWeakReference(aInstancePtr);
return mRoot->nsSupportsWeakReference::GetWeakReference(aInstancePtr);
}
NS_IMETHODIMP

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

@ -247,6 +247,10 @@ nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(XPCCallContext& ccx,
AUTO_MARK_JSVAL(ccx, fun);
// Ensure that we are asking for a scriptable interface.
// NB: It's important for security that this check is here rather
// than later, since it prevents untrusted objects from implementing
// some interfaces in JS and aggregating a trusted object to
// implement intentionally (for security) unscriptable interfaces.
// We so often ask for nsISupports that we can short-circuit the test...
if(!aIID.Equals(NS_GET_IID(nsISupports)))
{

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

@ -3315,7 +3315,9 @@ NS_IMETHODIMP nsDocViewerSelectionListener::NotifySelectionChanged(nsIDOMDocumen
}
//nsDocViewerFocusListener
NS_IMPL_ISUPPORTS1(nsDocViewerFocusListener, nsIDOMFocusListener)
NS_IMPL_ISUPPORTS2(nsDocViewerFocusListener,
nsIDOMFocusListener,
nsIDOMEventListener)
nsDocViewerFocusListener::nsDocViewerFocusListener()
:mDocViewer(nsnull)