Bug 368835 No focus events from xul tree table when a row is deleted, r=evan.yan, Olli.Pettay, sr=jonas, a=beltzner
authorsurkov.alexander@gmail.com
Tue, 11 Dec 2007 00:18:04 -0800
changeset 8868 62893cebff0b53634c36f65375018266d6288b66
parent 8867 e7a21b487e25ad753926d881149d57f2e365e891
child 8869 964d431d2d5fa2c385177c8cabadb5d0d8331c88
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherderautoland@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersevan.yan, Olli.Pettay, jonas, beltzner
bugs368835
milestone1.9b2pre
Bug 368835 No focus events from xul tree table when a row is deleted, r=evan.yan, Olli.Pettay, sr=jonas, a=beltzner
accessible/public/nsIAccessibleTreeCache.idl
accessible/src/atk/nsXULTreeAccessibleWrap.cpp
accessible/src/atk/nsXULTreeAccessibleWrap.h
accessible/src/base/nsRootAccessible.cpp
accessible/src/xul/nsXULTreeAccessible.cpp
accessible/src/xul/nsXULTreeAccessible.h
content/events/public/nsIPrivateDOMEvent.h
content/events/public/nsPLDOMEvent.h
content/events/src/Makefile.in
content/events/src/nsDOMDataContainerEvent.cpp
content/events/src/nsDOMDataContainerEvent.h
content/events/src/nsEventDispatcher.cpp
content/events/src/nsPLDOMEvent.cpp
content/events/test/Makefile.in
content/events/test/test_bug368835.html
dom/public/idl/events/Makefile.in
dom/public/idl/events/nsIDOMDataContainerEvent.idl
dom/public/nsDOMClassInfoID.h
dom/src/base/nsDOMClassInfo.cpp
layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
layout/xul/base/src/tree/src/nsTreeBodyFrame.h
layout/xul/test/Makefile.in
layout/xul/test/test_bug368835.xul
--- a/accessible/public/nsIAccessibleTreeCache.idl
+++ b/accessible/public/nsIAccessibleTreeCache.idl
@@ -32,22 +32,39 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #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);
 };
+
--- a/accessible/src/atk/nsXULTreeAccessibleWrap.cpp
+++ b/accessible/src/atk/nsXULTreeAccessibleWrap.cpp
@@ -442,17 +442,17 @@ NS_IMETHODIMP nsXULTreeAccessibleWrap::C
 
 NS_IMETHODIMP nsXULTreeAccessibleWrap::IsProbablyForLayout(PRBool *aIsProbablyForLayout)
 {
   *aIsProbablyForLayout = PR_FALSE;
   return NS_OK;
 }
 
 // --------------------------------------------------------
-// nsXULTreeAccessibleWrap Accessible
+// nsXULTreeColumnsAccessibleWrap Accessible
 // --------------------------------------------------------
 NS_IMPL_ISUPPORTS_INHERITED1(nsXULTreeColumnsAccessibleWrap, nsXULTreeColumnsAccessible, nsIAccessibleTable)
 
 nsXULTreeColumnsAccessibleWrap::nsXULTreeColumnsAccessibleWrap(nsIDOMNode *aDOMNode, nsIWeakReference *aShell):
 nsXULTreeColumnsAccessible(aDOMNode, aShell)
 {
 }
 
--- a/accessible/src/atk/nsXULTreeAccessibleWrap.h
+++ b/accessible/src/atk/nsXULTreeAccessibleWrap.h
@@ -50,18 +50,22 @@ class nsXULTreeAccessibleWrap : public n
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIACCESSIBLETABLE
 
   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,
                                        public nsIAccessibleTable
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIACCESSIBLETABLE
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -48,16 +48,17 @@
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsIDOMHTMLImageElement.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLSelectElement.h"
+#include "nsIDOMDataContainerEvent.h"
 #include "nsIDOMNSEvent.h"
 #include "nsIDOMXULMenuListElement.h"
 #include "nsIDOMXULMultSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIDOMXULPopupElement.h"
 #include "nsIDocument.h"
 #include "nsIEventListenerManager.h"
 #include "nsIFocusController.h"
@@ -282,16 +283,17 @@ const char* const docEvents[] = {
   // capture NameChange events (fired whenever name changes, immediately after, whether focus moves or not)
   "NameChange",
   // capture ValueChange events (fired whenever value changes, immediately after, whether focus moves or not)
   "ValueChange",
   // capture AlertActive events (fired whenever alert pops up)
   "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)
   "CheckboxStateChange",
   // add ourself as a RadioStateChange Listener ( custom event fired in in nsHTMLInputElement.cpp  & radio.xml)
   "RadioStateChange",
   "popupshown",
   "popuphiding",
@@ -636,30 +638,59 @@ nsresult nsRootAccessible::HandleEventWi
     // Don't create the doc accessible until load scripts have a chance to set
     // role attribute for <body> or <html> element, because the value of 
     // role attribute will be cached when the doc accessible is Init()'d
     TryFireEarlyLoadEvent(aTargetNode);
     return NS_OK;
   }
 
   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,
                                             nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE);
   }
 
   nsCOMPtr<nsIAccessible> accessible;
   accService->GetAccessibleInShell(aTargetNode, eventShell,
                                    getter_AddRefs(accessible));
   nsCOMPtr<nsPIAccessible> privAcc(do_QueryInterface(accessible));
   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);
 
     // radiogroup in prefWindow is exposed as a list,
     // and panebutton is exposed as XULListitem in A11y.
     // nsXULListitemAccessible::GetState uses STATE_SELECTED in this case,
     // so we need to check nsIAccessibleStates::STATE_SELECTED also.
     PRBool isEnabled = (state & (nsIAccessibleStates::STATE_CHECKED |
--- a/accessible/src/xul/nsXULTreeAccessible.cpp
+++ b/accessible/src/xul/nsXULTreeAccessible.cpp
@@ -44,18 +44,16 @@
 #include "nsXULTreeAccessibleWrap.h"
 #include "nsIMutableArray.h"
 #include "nsComponentManagerUtils.h"
 
 #ifdef MOZ_ACCESSIBILITY_ATK
 #include "nsIAccessibleTable.h"
 #endif
 
-#define kMaxTreeColumns 100
-
 /* static */
 PRBool nsXULTreeAccessible::IsColumnHidden(nsITreeColumn *aColumn)
 {
   nsCOMPtr<nsIDOMElement> element;
   aColumn->GetElement(getter_AddRefs(element));
   nsCOMPtr<nsIContent> content = do_QueryInterface(element);
   return content->AttrValueIs(kNameSpaceID_None, nsAccessibilityAtoms::hidden,
                               nsAccessibilityAtoms::_true, eCaseMatters);
@@ -501,16 +499,18 @@ NS_IMETHODIMP nsXULTreeAccessible::Selec
       if (selection)
         selection->SelectAll();
     }
   }
 
   return NS_OK;
 }
 
+// nsIAccessible nsIAccessibleTreeCache::
+//   GetCachedTreeitemAccessible(in long aRow, nsITreeColumn* aColumn)
 NS_IMETHODIMP
 nsXULTreeAccessible::GetCachedTreeitemAccessible(PRInt32 aRow,
                                                  nsITreeColumn* aColumn,
                                                  nsIAccessible** aAccessible)
 {
   NS_ENSURE_ARG_POINTER(aAccessible);
   *aAccessible = nsnull;
 
@@ -549,16 +549,87 @@ nsXULTreeAccessible::GetCachedTreeitemAc
     NS_ENSURE_SUCCESS(rv, rv);
     PutCacheEntry(*mAccessNodeCache, (void*)(aRow * kMaxTreeColumns + columnIndex), accessNode);
   }
   nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
   NS_IF_ADDREF(*aAccessible = accessible);
   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);
   nsCOMPtr<nsITreeColumns> treeColumns;
   aBoxObject->GetColumns(getter_AddRefs(treeColumns));
   NS_ENSURE_TRUE(treeColumns, NS_ERROR_FAILURE);
   return treeColumns->GetCount(aCount);
 }
--- a/accessible/src/xul/nsXULTreeAccessible.h
+++ b/accessible/src/xul/nsXULTreeAccessible.h
@@ -39,20 +39,20 @@
 #define __nsXULTreeAccessible_h__
 
 #include "nsITreeBoxObject.h"
 #include "nsITreeView.h"
 #include "nsITreeColumns.h"
 #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,
                             public nsIAccessibleTreeCache
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIACCESSIBLESELECTABLE
--- a/content/events/public/nsIPrivateDOMEvent.h
+++ b/content/events/public/nsIPrivateDOMEvent.h
@@ -70,16 +70,18 @@ public:
   NS_IMETHOD SetTrusted(PRBool aTrusted)=0;
 };
 
 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);
 nsresult
 NS_NewDOMKeyboardEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsKeyEvent *aEvent);
 nsresult
 NS_NewDOMMutationEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsMutationEvent* aEvent);
 nsresult
--- a/content/events/public/nsPLDOMEvent.h
+++ b/content/events/public/nsPLDOMEvent.h
@@ -54,17 +54,22 @@
  *       structure used with the old eventing system.  See bug 334573.
  */
  
 class nsPLDOMEvent : public nsRunnable {
 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
--- a/content/events/src/Makefile.in
+++ b/content/events/src/Makefile.in
@@ -64,16 +64,17 @@ REQUIRES	= xpcom \
 		  unicharutil \
 		  imglib2 \
 		  $(NULL)
 
 CPPSRCS		= \
 		nsEventListenerManager.cpp \
 		nsEventStateManager.cpp \
 		nsDOMEvent.cpp \
+		nsDOMDataContainerEvent.cpp \
 		nsDOMUIEvent.cpp \
 		nsDOMKeyboardEvent.cpp \
 		nsDOMTextEvent.cpp \
 		nsDOMMouseEvent.cpp \
 		nsDOMMutationEvent.cpp \
 		nsDOMPopupBlockedEvent.cpp \
 		nsDOMBeforeUnloadEvent.cpp \
 		nsDOMPageTransitionEvent.cpp \
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMDataContainerEvent.cpp
@@ -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;
+}
+
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMDataContainerEvent.h
@@ -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
+
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -643,11 +643,14 @@ nsEventDispatcher::CreateEvent(nsPresCon
     return NS_NewDOMSVGZoomEvent(aDOMEvent, aPresContext, nsnull);
 #endif // MOZ_SVG
   if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") ||
       aEventType.LowerCaseEqualsLiteral("xulcommandevents"))
     return NS_NewDOMXULCommandEvent(aDOMEvent, aPresContext, nsnull);
   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;
 }
--- a/content/events/src/nsPLDOMEvent.cpp
+++ b/content/events/src/nsPLDOMEvent.cpp
@@ -42,34 +42,37 @@
 #include "nsIDOMDocumentEvent.h"
 #include "nsIDOMEventTarget.h"
 
 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;
 }
 
 nsresult nsPLDOMEvent::PostDOMEvent()
 {
   return NS_DispatchToCurrentThread(this);
 }
--- a/content/events/test/Makefile.in
+++ b/content/events/test/Makefile.in
@@ -46,16 +46,17 @@ include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		test_bug238987.html \
 		test_bug288392.html \
 		test_bug336682_1.html \
 		test_bug336682_2.xul \
 		test_bug336682.js \
 		test_bug367781.html \
+		test_bug368835.html \
 		test_bug379120.html \
 		test_bug391568.xhtml \
 		test_bug402089.html \
 		test_bug405632.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_bug368835.html
@@ -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>
+
--- a/dom/public/idl/events/Makefile.in
+++ b/dom/public/idl/events/Makefile.in
@@ -56,16 +56,17 @@ SDK_XPIDLSRCS =                         
 	nsIDOMEventGroup.idl			\
 	nsIDOMCustomEvent.idl			\
 	nsIDOMMouseEvent.idl			\
 	nsIDOMUIEvent.idl			\
 	$(NULL)
 
 XPIDLSRCS =					\
 	nsIDOMNSEvent.idl			\
+	nsIDOMDataContainerEvent.idl	\
 	nsIDOMKeyEvent.idl			\
 	nsIDOMMutationEvent.idl			\
 	nsIDOMNSUIEvent.idl			\
 	nsIDOMPopupBlockedEvent.idl		\
 	nsIDOMBeforeUnloadEvent.idl		\
 	nsIDOMNSEventTarget.idl			\
 	nsIDOMSmartCardEvent.idl                \
 	nsIDOMPageTransitionEvent.idl		\
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/events/nsIDOMDataContainerEvent.idl
@@ -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);
+};
+
--- a/dom/public/nsDOMClassInfoID.h
+++ b/dom/public/nsDOMClassInfoID.h
@@ -348,17 +348,17 @@ enum nsDOMClassInfoID {
 
   // Canvas
   eDOMClassInfo_HTMLCanvasElement_id,
 #ifdef MOZ_ENABLE_CANVAS
   eDOMClassInfo_CanvasRenderingContext2D_id,
   eDOMClassInfo_CanvasGradient_id,
   eDOMClassInfo_CanvasPattern_id,
 #endif
-  
+
   // SmartCard Events
   eDOMClassInfo_SmartCardEvent_id,
   
   // PageTransition Events
   eDOMClassInfo_PageTransitionEvent_id,
 
   // WindowUtils
   eDOMClassInfo_WindowUtils_id,
@@ -411,16 +411,19 @@ enum nsDOMClassInfoID {
 
   eDOMClassInfo_FileList_id,
   eDOMClassInfo_File_id,
   eDOMClassInfo_FileException_id,
 
   // 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
 };
 
 /**
  * nsIClassInfo helper macros
  */
 
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -221,16 +221,17 @@
 #include "nsIDOMDocumentEvent.h"
 #include "nsIDOMAttr.h"
 #include "nsIDOMText.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMCDATASection.h"
 #include "nsIDOMProcessingInstruction.h"
 #include "nsIDOMNotation.h"
 #include "nsIDOMNSEvent.h"
+#include "nsIDOMDataContainerEvent.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMCommandEvent.h"
 #include "nsIDOMPopupBlockedEvent.h"
 #include "nsIDOMBeforeUnloadEvent.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIDOMSmartCardEvent.h"
 #include "nsIDOMXULCommandEvent.h"
@@ -1216,16 +1217,19 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(File, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(FileException, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   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();|
 struct nsContractIDMapData
 {
   PRInt32 mDOMClassInfoID;
   const char *mContractID;
 };
@@ -3344,16 +3348,20 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMViewCSS)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMAbstractView)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow)
     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);
 
     if (i != eDOMClassInfoIDCount) {
       NS_ERROR("The number of items in sClassInfoData doesn't match the "
                "number of nsIDOMClassInfo ID's, this is bad! Fix it!");
 
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
@@ -56,20 +56,25 @@
 
 #include "nsGkAtoms.h"
 #include "nsCSSAnonBoxes.h"
 
 #include "nsIContent.h"
 #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"
 #include "nsICSSStyleRule.h"
 #include "nsCSSRendering.h"
 #include "nsIFontMetrics.h"
 #include "nsIDeviceContext.h"
 #include "nsIXULTemplateBuilder.h"
@@ -1785,16 +1790,22 @@ nsTreeBodyFrame::CreateTimer(const nsILo
   return NS_OK;
 }
 
 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));
   if (sel)
     sel->AdjustSelection(aIndex, aCount);
 
   if (mUpdateBatchNest)
     return NS_OK;
@@ -4360,16 +4371,73 @@ nsTreeBodyFrame::PostScrollEvent()
   nsRefPtr<ScrollEvent> ev = new ScrollEvent(this);
   if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
     NS_WARNING("failed to dispatch ScrollEvent");
   } else {
     mScrollEvent = ev;
   }
 }
 
+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)
 {
   ScrollParts parts = GetScrollParts();
   nsWeakFrame weakFrame(this);
   UpdateScrollbars(parts);
   NS_ENSURE_TRUE(weakFrame.IsAlive(), PR_FALSE);
   if (aNeedsFullInvalidation) {
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.h
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.h
@@ -408,16 +408,27 @@ protected:
     void Revoke() { mInner = nsnull; }
   private:
     nsTreeBodyFrame* mInner;
   };
 
   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;
 
   // Cached column information.
   nsRefPtr<nsTreeColumns> mColumns;
 
   // The current view for this tree widget.  We get all of our row and cell data
--- a/layout/xul/test/Makefile.in
+++ b/layout/xul/test/Makefile.in
@@ -39,17 +39,19 @@ DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 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 \
 		test_bug398982-2.xul \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/layout/xul/test/test_bug368835.xul
@@ -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>
+