Bug 368835 No focus events from xul tree table when a row is deleted, r=evan.yan, Olli.Pettay, sr=jonas, a=beltzner

This commit is contained in:
surkov.alexander@gmail.com 2007-12-11 00:18:04 -08:00
parent 058b8cbbd5
commit 45253210b5
23 changed files with 754 additions and 30 deletions

View File

@ -37,17 +37,34 @@
#include "nsITreeColumns.idl"
interface nsIAccessible;
/**
* A cross-platform interface that supports cache for tree item
*
* @status UNDER_REVIEW
*/
[scriptable, uuid(CC742DA2-9C25-4D04-96CD-DA407D676C6D)]
[uuid(7cdad914-948b-4bbc-9c47-ee5e1ae6b148)]
interface nsIAccessibleTreeCache : nsISupports
{
/**
* Get tree item from cache according to row and column, create if doesn't exist in cache
* "aColumn" can be nsnull
* Get tree item from cache according to row and column, create if doesn't
* exist in cache.
*
* @param aRow the given row index
* @param aColumn the given column object. If is is nsnull then primary
* column is used. It makes sense for ATK only.
*/
[noscript] nsIAccessible getCachedTreeitemAccessible(in PRInt32 aRow, in nsITreeColumn aColumn);
nsIAccessible getCachedTreeitemAccessible(in long aRow,
in nsITreeColumn aColumn);
/**
* Invalidates the number of cached treeitem accessibles.
*
* @param aRow row index the invalidation starts from
* @param aCount the number of treeitem accessibles to invalidate,
* the number sign specifies whether rows have been
* inserted (plus) or removed (minus)
*/
void invalidateCache(in long aRow, in long aCount);
};

View File

@ -447,7 +447,7 @@ NS_IMETHODIMP nsXULTreeAccessibleWrap::IsProbablyForLayout(PRBool *aIsProbablyFo
}
// --------------------------------------------------------
// nsXULTreeAccessibleWrap Accessible
// nsXULTreeColumnsAccessibleWrap Accessible
// --------------------------------------------------------
NS_IMPL_ISUPPORTS_INHERITED1(nsXULTreeColumnsAccessibleWrap, nsXULTreeColumnsAccessible, nsIAccessibleTable)

View File

@ -55,8 +55,12 @@ public:
nsXULTreeAccessibleWrap(nsIDOMNode* aDOMNode, nsIWeakReference* aShell);
virtual ~nsXULTreeAccessibleWrap() {}
// nsIAccessible
NS_IMETHOD GetChildCount(PRInt32 *_retval);
NS_IMETHOD ChangeSelection(PRInt32 aIndex, PRUint8 aMethod, PRBool *aSelState);
protected:
NS_IMETHOD ChangeSelection(PRInt32 aIndex, PRUint8 aMethod,
PRBool *aSelState);
};
class nsXULTreeColumnsAccessibleWrap : public nsXULTreeColumnsAccessible,

View File

@ -53,6 +53,7 @@
#include "nsIDOMHTMLImageElement.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMHTMLSelectElement.h"
#include "nsIDOMDataContainerEvent.h"
#include "nsIDOMNSEvent.h"
#include "nsIDOMXULMenuListElement.h"
#include "nsIDOMXULMultSelectCntrlEl.h"
@ -287,6 +288,7 @@ const char* const docEvents[] = {
"AlertActive",
// add ourself as a TreeViewChanged listener (custom event fired in nsTreeBodyFrame.cpp)
"TreeViewChanged",
"TreeRowCountChanged",
// add ourself as a OpenStateChange listener (custom event fired in tree.xml)
"OpenStateChange",
// add ourself as a CheckboxStateChange listener (custom event fired in nsHTMLInputElement.cpp)
@ -641,7 +643,9 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
}
if (eventType.EqualsLiteral("TreeViewChanged")) { // Always asynch, always from user input
NS_ENSURE_TRUE(localName.EqualsLiteral("tree"), NS_OK);
if (!localName.EqualsLiteral("tree"))
return NS_OK;
nsCOMPtr<nsIContent> treeContent = do_QueryInterface(aTargetNode);
nsAccEvent::PrepareForEvent(aTargetNode, PR_TRUE);
return accService->InvalidateSubtreeFor(eventShell, treeContent,
@ -655,6 +659,33 @@ nsresult nsRootAccessible::HandleEventWithTarget(nsIDOMEvent* aEvent,
if (!privAcc)
return NS_OK;
if (eventType.EqualsLiteral("TreeRowCountChanged")) {
if (!localName.EqualsLiteral("tree"))
return NS_OK;
nsCOMPtr<nsIDOMDataContainerEvent> dataEvent(do_QueryInterface(aEvent));
NS_ENSURE_STATE(dataEvent);
nsCOMPtr<nsIVariant> indexVariant;
dataEvent->GetData(NS_LITERAL_STRING("index"),
getter_AddRefs(indexVariant));
NS_ENSURE_STATE(indexVariant);
nsCOMPtr<nsIVariant> countVariant;
dataEvent->GetData(NS_LITERAL_STRING("count"),
getter_AddRefs(countVariant));
NS_ENSURE_STATE(countVariant);
PRInt32 index, count;
indexVariant->GetAsInt32(&index);
countVariant->GetAsInt32(&count);
nsCOMPtr<nsIAccessibleTreeCache> treeAccCache(do_QueryInterface(accessible));
NS_ENSURE_STATE(treeAccCache);
return treeAccCache->InvalidateCache(index, count);
}
if (eventType.EqualsLiteral("RadioStateChange")) {
PRUint32 state = State(accessible);

View File

@ -49,8 +49,6 @@
#include "nsIAccessibleTable.h"
#endif
#define kMaxTreeColumns 100
/* static */
PRBool nsXULTreeAccessible::IsColumnHidden(nsITreeColumn *aColumn)
{
@ -506,6 +504,8 @@ NS_IMETHODIMP nsXULTreeAccessible::SelectAllSelection(PRBool *_retval)
return NS_OK;
}
// nsIAccessible nsIAccessibleTreeCache::
// GetCachedTreeitemAccessible(in long aRow, nsITreeColumn* aColumn)
NS_IMETHODIMP
nsXULTreeAccessible::GetCachedTreeitemAccessible(PRInt32 aRow,
nsITreeColumn* aColumn,
@ -554,6 +554,77 @@ nsXULTreeAccessible::GetCachedTreeitemAccessible(PRInt32 aRow,
return NS_OK;
}
// void nsIAccessibleTreeCache::
// invalidateCache(in PRInt32 aRow, in PRInt32 aCount)
NS_IMETHODIMP
nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount)
{
// Do not invalidate the cache if rows have been inserted.
if (aCount > 0)
return NS_OK;
nsCOMPtr<nsITreeColumns> cols;
nsresult rv = mTree->GetColumns(getter_AddRefs(cols));
NS_ENSURE_SUCCESS(rv, rv);
#ifdef MOZ_ACCESSIBILITY_ATK
PRInt32 colsCount = 0;
rv = cols->GetCount(&colsCount);
NS_ENSURE_SUCCESS(rv, rv);
#else
nsCOMPtr<nsITreeColumn> col;
rv = cols->GetKeyColumn(getter_AddRefs(col));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 colIdx = 0;
rv = col->GetIndex(&colIdx);
NS_ENSURE_SUCCESS(rv, rv);
#endif
for (PRInt32 rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) {
#ifdef MOZ_ACCESSIBILITY_ATK
for (PRInt32 colIdx = 0; colIdx < colsCount; ++colIdx) {
#else
{
#endif
void *key = reinterpret_cast<void*>(rowIdx * kMaxTreeColumns + colIdx);
nsCOMPtr<nsIAccessNode> accessNode;
GetCacheEntry(*mAccessNodeCache, key, getter_AddRefs(accessNode));
if (accessNode) {
nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
nsCOMPtr<nsIAccessibleEvent> event =
new nsAccEvent(nsIAccessibleEvent::EVENT_DOM_DESTROY,
accessible, nsnull, PR_FALSE);
FireAccessibleEvent(event);
mAccessNodeCache->Remove(key);
}
}
}
PRInt32 newRowCount = 0;
rv = mTreeView->GetRowCount(&newRowCount);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 oldRowCount = newRowCount - aCount;
for (PRInt32 rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) {
#ifdef MOZ_ACCESSIBILITY_ATK
for (PRInt32 colIdx = 0; colIdx < colsCount; ++colIdx) {
#else
{
#endif
void *key = reinterpret_cast<void*>(rowIdx * kMaxTreeColumns + colIdx);
mAccessNodeCache->Remove(key);
}
}
return NS_OK;
}
nsresult nsXULTreeAccessible::GetColumnCount(nsITreeBoxObject* aBoxObject, PRInt32* aCount)
{
NS_ENSURE_TRUE(aBoxObject, NS_ERROR_FAILURE);

View File

@ -44,10 +44,10 @@
#include "nsXULSelectAccessible.h"
#include "nsIAccessibleTreeCache.h"
/*
* A class the represents the XUL Tree widget.
*/
const PRUint32 kMaxTreeColumns = 100;
const PRUint32 kDefaultTreeCacheSize = 256;
class nsXULTreeAccessible : public nsXULSelectableAccessible,

View File

@ -75,6 +75,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIPrivateDOMEvent, NS_IPRIVATEDOMEVENT_IID)
nsresult
NS_NewDOMEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent *aEvent);
nsresult
NS_NewDOMDataContainerEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent *aEvent);
nsresult
NS_NewDOMUIEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsGUIEvent *aEvent);
nsresult
NS_NewDOMMouseEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsInputEvent *aEvent);

View File

@ -59,12 +59,17 @@ public:
nsPLDOMEvent (nsIDOMNode *aEventNode, const nsAString& aEventType)
: mEventNode(aEventNode), mEventType(aEventType)
{ }
nsPLDOMEvent(nsIDOMNode *aEventNode, nsIDOMEvent *aEvent)
: mEventNode(aEventNode), mEvent(aEvent)
{ }
NS_IMETHOD Run();
nsresult PostDOMEvent();
nsCOMPtr<nsIDOMNode> mEventNode;
nsString mEventType;
nsCOMPtr<nsIDOMNode> mEventNode;
nsCOMPtr<nsIDOMEvent> mEvent;
nsString mEventType;
};
#endif

View File

@ -69,6 +69,7 @@ CPPSRCS = \
nsEventListenerManager.cpp \
nsEventStateManager.cpp \
nsDOMEvent.cpp \
nsDOMDataContainerEvent.cpp \
nsDOMUIEvent.cpp \
nsDOMKeyboardEvent.cpp \
nsDOMTextEvent.cpp \

View File

@ -0,0 +1,118 @@
/* -*- 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
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* 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 ***** */
#include "nsDOMDataContainerEvent.h"
#include "nsContentUtils.h"
nsDOMDataContainerEvent::nsDOMDataContainerEvent(nsPresContext *aPresContext,
nsEvent *aEvent)
: nsDOMEvent(aPresContext, aEvent)
{
mData.Init();
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMDataContainerEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDataContainerEvent,
nsDOMEvent)
if (tmp->mData.IsInitialized())
tmp->mData.Clear();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMDataContainerEvent,
nsDOMEvent)
tmp->mData.EnumerateRead(TraverseEntry, &cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(nsDOMDataContainerEvent, nsDOMEvent)
NS_IMPL_RELEASE_INHERITED(nsDOMDataContainerEvent, nsDOMEvent)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDataContainerEvent)
NS_INTERFACE_MAP_ENTRY(nsIDOMDataContainerEvent)
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(DataContainerEvent)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
NS_IMETHODIMP
nsDOMDataContainerEvent::GetData(const nsAString& aKey, nsIVariant **aData)
{
NS_ENSURE_ARG_POINTER(aData);
NS_ENSURE_STATE(mData.IsInitialized());
mData.Get(aKey, aData);
return NS_OK;
}
NS_IMETHODIMP
nsDOMDataContainerEvent::SetData(const nsAString& aKey, nsIVariant *aData)
{
NS_ENSURE_ARG(aData);
// Make sure this event isn't already being dispatched.
NS_ENSURE_STATE(!(NS_IS_EVENT_IN_DISPATCH(mEvent) ||
(mEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH_IMMEDIATELY)));
NS_ENSURE_STATE(mData.IsInitialized());
return mData.Put(aKey, aData) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
nsresult
NS_NewDOMDataContainerEvent(nsIDOMEvent** aInstancePtrResult,
nsPresContext* aPresContext,
nsEvent* aEvent)
{
nsDOMDataContainerEvent* it =
new nsDOMDataContainerEvent(aPresContext, aEvent);
NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY);
return CallQueryInterface(it, aInstancePtrResult);
}
PLDHashOperator
nsDOMDataContainerEvent::TraverseEntry(const nsAString& aKey,
nsIVariant *aDataItem,
void* aUserArg)
{
nsCycleCollectionTraversalCallback *cb =
static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
cb->NoteXPCOMChild(aDataItem);
return PL_DHASH_NEXT;
}

View File

@ -0,0 +1,67 @@
/* -*- 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
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* 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 nsDOMDataContainerEvent_h___
#define nsDOMDataContainerEvent_h___
#include "nsIDOMDataContainerEvent.h"
#include "nsDOMEvent.h"
class nsDOMDataContainerEvent : public nsDOMEvent,
public nsIDOMDataContainerEvent
{
public:
nsDOMDataContainerEvent(nsPresContext* aPresContext, nsEvent* aEvent);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMDataContainerEvent, nsDOMEvent)
NS_FORWARD_TO_NSDOMEVENT
NS_DECL_NSIDOMDATACONTAINEREVENT
private:
static PLDHashOperator PR_CALLBACK
TraverseEntry(const nsAString& aKey, nsIVariant *aDataItem, void* aUserArg);
nsInterfaceHashtable<nsStringHashKey, nsIVariant> mData;
};
#endif

View File

@ -648,6 +648,9 @@ nsEventDispatcher::CreateEvent(nsPresContext* aPresContext,
if (aEventType.LowerCaseEqualsLiteral("commandevent") ||
aEventType.LowerCaseEqualsLiteral("commandevents"))
return NS_NewDOMCommandEvent(aDOMEvent, aPresContext, nsnull);
if (aEventType.LowerCaseEqualsLiteral("datacontainerevent") ||
aEventType.LowerCaseEqualsLiteral("datacontainerevents"))
return NS_NewDOMDataContainerEvent(aDOMEvent, aPresContext, nsnull);
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}

View File

@ -47,25 +47,28 @@ NS_IMETHODIMP nsPLDOMEvent::Run()
if (!mEventNode) {
return NS_OK;
}
nsCOMPtr<nsIDOMDocument> domDoc;
mEventNode->GetOwnerDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDOMDocumentEvent> domEventDoc = do_QueryInterface(domDoc);
if (domEventDoc) {
nsCOMPtr<nsIDOMEvent> domEvent;
domEventDoc->CreateEvent(NS_LITERAL_STRING("Events"),
getter_AddRefs(domEvent));
nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(domEvent));
if (privateEvent && NS_SUCCEEDED(domEvent->InitEvent(mEventType, PR_TRUE, PR_TRUE))) {
privateEvent->SetTrusted(PR_TRUE);
nsCOMPtr<nsIDOMEvent> domEvent(mEvent);
if (!domEvent) {
nsCOMPtr<nsIDOMDocument> domDoc;
mEventNode->GetOwnerDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDOMDocumentEvent> domEventDoc = do_QueryInterface(domDoc);
if (domEventDoc) {
domEventDoc->CreateEvent(NS_LITERAL_STRING("Events"),
getter_AddRefs(domEvent));
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mEventNode);
PRBool defaultActionEnabled; // This is not used because the caller is async
target->DispatchEvent(domEvent, &defaultActionEnabled);
nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(domEvent));
if (privateEvent &&
NS_SUCCEEDED(domEvent->InitEvent(mEventType, PR_TRUE, PR_TRUE))) {
privateEvent->SetTrusted(PR_TRUE);
}
}
}
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mEventNode);
PRBool defaultActionEnabled; // This is not used because the caller is async
target->DispatchEvent(domEvent, &defaultActionEnabled);
return NS_OK;
}

View File

@ -51,6 +51,7 @@ _TEST_FILES = \
test_bug336682_2.xul \
test_bug336682.js \
test_bug367781.html \
test_bug368835.html \
test_bug379120.html \
test_bug391568.xhtml \
test_bug402089.html \

View File

@ -0,0 +1,87 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=368835
-->
<head>
<title>Test for Bug 368835</title>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=368835">
Mozilla Bug 368835
</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
function dataContainerEventHandler(aEvent)
{
var value = "";
var isPassed = true;
try {
value = aEvent.getData("data1");
isPassed = true;
} catch (e) {
isPassed = false;
}
ok(isPassed, "getData shouldn't fail.");
ok(value == "data1", "Wrong value of data.");
try {
aEvent.setData("data3", "data3");
isPassed = false;
} catch (e) {
isPassed = true;
}
ok(isPassed, "setData should fail during event dispatching.");
}
function doTest()
{
var isPassed;
var event = null;
try {
event = document.createEvent("datacontainerevents");
isPassed = true;
} catch (e) {
isPassed = false;
}
ok(isPassed, "Document should know about 'datacontainerevents' event class.");
ok(("setData" in event), "nsIDOMDataContainerEvent isn't available.");
event.initEvent("dataContainerEvent", true, true);
try {
event.setData("data1", "data1");
isPassed = true;
} catch (e) {
isPassed = false;
}
ok(isPassed, "setData shouldn't fail when event is initialized.");
document.body.addEventListener("dataContainerEvent",
dataContainerEventHandler, true);
document.body.dispatchEvent(event);
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
addLoadEvent(SimpleTest.finish);
</script>
</pre>
</body>
</html>

View File

@ -61,6 +61,7 @@ SDK_XPIDLSRCS = \
XPIDLSRCS = \
nsIDOMNSEvent.idl \
nsIDOMDataContainerEvent.idl \
nsIDOMKeyEvent.idl \
nsIDOMMutationEvent.idl \
nsIDOMNSUIEvent.idl \

View File

@ -0,0 +1,63 @@
/* -*- 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
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* 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 ***** */
#include "nsIDOMEvent.idl"
#include "nsIVariant.idl"
[scriptable, uuid(3600d66c-b9ac-4c22-b39a-d64cce619921)]
interface nsIDOMDataContainerEvent : nsIDOMEvent
{
/**
* Return the data associated with the given key.
*
* @param key the key
* @return the data associated with the key
*/
nsIVariant getData(in DOMString key);
/**
* Set the data for the given key.
*
* @param key the data key
* @param data the data
* @throws NS_ERROR_UNEXPECTED if the method is called during event
* dispatch
*/
void setData(in DOMString key, in nsIVariant data);
};

View File

@ -353,7 +353,7 @@ enum nsDOMClassInfoID {
eDOMClassInfo_CanvasGradient_id,
eDOMClassInfo_CanvasPattern_id,
#endif
// SmartCard Events
eDOMClassInfo_SmartCardEvent_id,
@ -416,6 +416,9 @@ enum nsDOMClassInfoID {
// DOM modal content window class, almost identical to Window
eDOMClassInfo_ModalContentWindow_id,
// Data Events
eDOMClassInfo_DataContainerEvent_id,
// This one better be the last one in this list
eDOMClassInfoIDCount
};

View File

@ -226,6 +226,7 @@
#include "nsIDOMProcessingInstruction.h"
#include "nsIDOMNotation.h"
#include "nsIDOMNSEvent.h"
#include "nsIDOMDataContainerEvent.h"
#include "nsIDOMKeyEvent.h"
#include "nsIDOMMouseEvent.h"
#include "nsIDOMCommandEvent.h"
@ -1221,6 +1222,9 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(ModalContentWindow, nsWindowSH,
DEFAULT_SCRIPTABLE_FLAGS |
WINDOW_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(DataContainerEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
};
// Objects that shuld be constructable through |new Name();|
@ -3349,6 +3353,10 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMModalContentWindow)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(DataContainerEvent, nsIDOMDataContainerEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataContainerEvent)
DOM_CLASSINFO_MAP_END
#ifdef NS_DEBUG
{
PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData);

View File

@ -61,10 +61,15 @@
#include "nsStyleContext.h"
#include "nsIBoxObject.h"
#include "nsGUIEvent.h"
#include "nsPLDOMEvent.h"
#include "nsIDOMDataContainerEvent.h"
#include "nsIDOMMouseEvent.h"
#include "nsIPrivateDOMEvent.h"
#include "nsIDOMElement.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMDocument.h"
#include "nsIDOMNSDocument.h"
#include "nsIDOMDocumentEvent.h"
#include "nsIDOMXULElement.h"
#include "nsIDocument.h"
#include "nsIContent.h"
@ -1790,6 +1795,12 @@ NS_IMETHODIMP nsTreeBodyFrame::RowCountChanged(PRInt32 aIndex, PRInt32 aCount)
if (aCount == 0 || !mView)
return NS_OK; // Nothing to do.
#ifdef ACCESSIBILITY
nsIPresShell *presShell = PresContext()->PresShell();
if (presShell->IsAccessibilityActive())
FireRowCountChangedEvent(aIndex, aCount);
#endif
// Adjust our selection.
nsCOMPtr<nsITreeSelection> sel;
mView->GetSelection(getter_AddRefs(sel));
@ -4365,6 +4376,63 @@ nsTreeBodyFrame::PostScrollEvent()
}
}
void
nsTreeBodyFrame::FireRowCountChangedEvent(PRInt32 aIndex, PRInt32 aCount)
{
nsCOMPtr<nsIContent> content(GetBaseElement());
if (!content)
return;
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(content));
nsCOMPtr<nsIDOMDocument> domDoc;
node->GetOwnerDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDOMDocumentEvent> domEventDoc(do_QueryInterface(domDoc));
if (!domEventDoc)
return;
nsCOMPtr<nsIDOMEvent> event;
domEventDoc->CreateEvent(NS_LITERAL_STRING("datacontainerevents"),
getter_AddRefs(event));
event->InitEvent(NS_LITERAL_STRING("TreeRowCountChanged"), PR_TRUE, PR_FALSE);
nsCOMPtr<nsIDOMDataContainerEvent> treeEvent(do_QueryInterface(event));
if (!treeEvent)
return;
// Set 'index' data - the row index rows are changed from.
nsCOMPtr<nsIWritableVariant> indexVariant(
do_CreateInstance("@mozilla.org/variant;1"));
if (!indexVariant)
return;
indexVariant->SetAsInt32(aIndex);
treeEvent->SetData(NS_LITERAL_STRING("index"), indexVariant);
// Set 'count' data - the number of changed rows.
nsCOMPtr<nsIWritableVariant> countVariant(
do_CreateInstance("@mozilla.org/variant;1"));
if (!countVariant)
return;
countVariant->SetAsInt32(aCount);
treeEvent->SetData(NS_LITERAL_STRING("count"), countVariant);
// Fire an event.
nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
if (!privateEvent)
return;
privateEvent->SetTrusted(PR_TRUE);
nsRefPtr<nsPLDOMEvent> plevent = new nsPLDOMEvent(node, event);
if (!plevent)
return;
plevent->PostDOMEvent();
}
PRBool
nsTreeBodyFrame::FullScrollbarsUpdate(PRBool aNeedsFullInvalidation)
{

View File

@ -413,6 +413,17 @@ protected:
void PostScrollEvent();
void FireScrollEvent();
/**
* Fires 'treeRowCountChanged' event asynchronously. The event supports
* nsIDOMDataContainerEvent interface that is used to expose the following
* information structures.
*
* @param aIndex the row index rows are added/removed from
* @param aCount the number of added/removed rows (the sign points to
* an operation, plus - addition, minus - removing)
*/
void FireRowCountChangedEvent(PRInt32 aIndex, PRInt32 aCount);
protected: // Data Members
// The cached box object parent.
nsCOMPtr<nsITreeBoxObject> mTreeBoxObject;

View File

@ -44,7 +44,9 @@ relativesrcdir = layout/xul/test
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = test_bug372685.xul \
_TEST_FILES =\
test_bug368835.xul \
test_bug372685.xul \
test_bug386386.html \
test_bug394800.xhtml \
test_bug398982-1.xul \

View File

@ -0,0 +1,158 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="/tests/SimpleTest/test.css" type="text/css"?>
<!--
Bug 368835 - fire TreeViewChanged/TreeRowCountChanged events.
-->
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Mozilla Bug 368835">
<script type="application/javascript" src="/MochiKit/packed.js"/>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript">
<![CDATA[
function inTreeView() { }
inTreeView.prototype =
{
mRowCount: 0,
mTree: null,
get rowCount() { return this.mRowCount; },
setTree: function(aTree) { this.mTree = aTree; },
getCellText: function(aRow, aCol) { return "hello"; },
getRowProperties: function(aIndex, aProperties) {},
getCellProperties: function(aIndex, aCol, aProperties) {},
getColumnProperties: function(aCol, aProperties) {},
getParentIndex: function(aRowIndex) { },
hasNextSibling: function(aRowIndex, aAfterIndex) { },
getLevel: function(aIndex) {},
getImageSrc: function(aRow, aCol) {},
getProgressMode: function(aRow, aCol) {},
getCellValue: function(aRow, aCol) {},
isContainer: function(aIndex) {},
isContainerOpen: function(aIndex) {},
isContainerEmpty: function(aIndex) {},
isSeparator: function(aIndex) {},
isSorted: function() {},
toggleOpenState: function(aIndex) {},
selectionChanged: function() {},
cycleHeader: function(aCol) {},
cycleCell: function(aRow, aCol) {},
isEditable: function(aRow, aCol) {},
isSelectable: function(aRow, aCol) {},
setCellValue: function(aRow, aCol, aValue) {},
setCellText: function(aRow, aCol, aValue) { },
performAction: function(aAction) {},
performActionOnRow: function(aAction, aRow) {},
performActionOnCell: function(aAction, aRow, aCol) {}
};
var gTreeViewChanged = false;
function TreeViewChangedHandler(aEvent)
{
gTreeViewChanged = true;
}
var gTreeRowCountChanged = false;
function TreeRowCountChangedHandler(aEvent)
{
netscape.security.PrivilegeManager.
enablePrivilege("UniversalXPConnect UniversalBrowserWrite");
gTreeRowCountChanged = true;
var index = aEvent.getData("index");
ok(index == 0, "Wrong 'index' data of 'treeRowCountChanged' event.");
var count = aEvent.getData("count");
ok(count == 1, "Wrong 'count' data of 'treeRowCountChanged' event.");
}
function CheckEvents()
{
netscape.security.PrivilegeManager.
enablePrivilege("UniversalXPConnect UniversalBrowserWrite");
// If these fail then it doesn't mean actually events are not fired,
// possibly setTimeout was executed earlier than events have beenS fired.
ok(gTreeViewChanged, "TreeViewChanged event should have been fired.")
ok(gTreeRowCountChanged, "TreeRowCountChanged event should have been fired.");
document.removeEventListener("TreeViewChanged",
TreeViewChangedHandler, true);
document.removeEventListener("TreeRowCountChanged",
TreeRowCountChangedHandler, true);
SimpleTest.finish();
}
var gAccService = null;
function doTest()
{
netscape.security.PrivilegeManager.
enablePrivilege("UniversalXPConnect UniversalBrowserWrite");
// Check whether accessbility support is enabled.
if (!("@mozilla.org/accessibleRetrieval;1" in Components.classes)) {
SimpleTest.finish();
return;
}
// Activate accessibility, otherwise events aren't fired.
gAccService = Components.classes["@mozilla.org/accessibleRetrieval;1"].
getService(Components.interfaces.nsIAccessibleRetrieval);
document.addEventListener("TreeViewChanged",
TreeViewChangedHandler, true);
document.addEventListener("TreeRowCountChanged",
TreeRowCountChangedHandler, true);
var tree = document.getElementById("tree");
var treeBox = tree.treeBoxObject;
var view = new inTreeView();
view.mRowCount = 5;
treeBox.view = view;
view.selection.currentIndex = 0;
view.selection.selectAll();
++view.mRowCount;
treeBox.rowCountChanged(0, 1);
if (gTreeViewChanged && gTreeRowCountChanged)
CheckEvents();
else
window.setTimeout(CheckEvents, 1000);
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(doTest);
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=368835">
Mozilla Bug 368835
</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
</body>
<tree id="tree" flex="1">
<treecols>
<treecol id="col" flex="1" primary="true" label="column"/>
</treecols>
<treechildren id="treechildren"/>
</tree>
</window>