Bug 660083 - missing focus event when opening a message in Thunderbird, r=tbsaunde
authorAlexander Surkov <surkov.alexander@gmail.com>
Thu, 27 Dec 2012 13:25:27 +0900
changeset 126190 af2c780aa93163939ba96dcdd142413f9cdcdd4d
parent 126189 e7b858cfccc27e4eaeade03d294f7e24bddb48f5
child 126191 580018b5f41b78f1fe73bb12f983b5b4bab2101d
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstbsaunde
bugs660083
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 660083 - missing focus event when opening a message in Thunderbird, r=tbsaunde
accessible/src/base/DocManager.cpp
accessible/src/base/NotificationController.cpp
accessible/src/generic/DocAccessible-inl.h
accessible/src/generic/DocAccessible.cpp
accessible/src/generic/DocAccessible.h
accessible/tests/mochitest/states/test_frames.html
--- a/accessible/src/base/DocManager.cpp
+++ b/accessible/src/base/DocManager.cpp
@@ -353,17 +353,17 @@ DocManager::CreateDocOrRootAccessible(ns
   // Ignore temporary, hiding, resource documents and documents without
   // docshell.
   if (aDocument->IsInitialDocument() || !aDocument->IsVisible() ||
       aDocument->IsResourceDoc() || !aDocument->IsActive())
     return nullptr;
 
   // Ignore documents without presshell and not having root frame.
   nsIPresShell* presShell = aDocument->GetShell();
-  if (!presShell || !presShell->GetRootFrame() || presShell->IsDestroying())
+  if (!presShell || presShell->IsDestroying())
     return nullptr;
 
   bool isRootDoc = nsCoreUtils::IsRootDocument(aDocument);
 
   DocAccessible* parentDocAcc = nullptr;
   if (!isRootDoc) {
     // XXXaaronl: ideally we would traverse the presshell chain. Since there's
     // no easy way to do that, we cheat and use the document hierarchy.
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "NotificationController.h"
 
 #include "Accessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
-#include "DocAccessible.h"
+#include "DocAccessible-inl.h"
 #include "nsEventShell.h"
 #include "FocusManager.h"
 #include "Role.h"
 #include "TextLeafAccessible.h"
 #include "TextUpdater.h"
 
 #ifdef A11Y_LOG
 #include "Logging.h"
@@ -208,16 +208,20 @@ NotificationController::WillRefresh(mozi
 #endif
 
     mDocument->DoInitialUpdate();
 
     NS_ASSERTION(mContentInsertions.Length() == 0,
                  "Pending content insertions while initial accessible tree isn't created!");
   }
 
+  // Initialize scroll support if needed.
+  if (!(mDocument->mDocFlags & DocAccessible::eScrollInitialized))
+    mDocument->AddScrollListener();
+
   // Process content inserted notifications to update the tree. Process other
   // notifications like DOM events and then flush event queue. If any new
   // notifications are queued during this processing then they will be processed
   // on next refresh. If notification processing queues up new events then they
   // are processed in this refresh. If events processing queues up new events
   // then new events are processed on next refresh.
   // Note: notification processing or event handling may shut down the owning
   // document accessible.
--- a/accessible/src/generic/DocAccessible-inl.h
+++ b/accessible/src/generic/DocAccessible-inl.h
@@ -61,16 +61,43 @@ DocAccessible::UpdateText(nsIContent* aT
   NS_ASSERTION(mNotificationController, "The document was shut down!");
 
   // Ignore the notification if initial tree construction hasn't been done yet.
   if (mNotificationController && HasLoadState(eTreeConstructed))
     mNotificationController->ScheduleTextUpdate(aTextNode);
 }
 
 inline void
+DocAccessible::AddScrollListener()
+{
+  // Delay scroll initializing until the document has a root frame.
+  if (!mPresShell->GetRootFrame())
+    return;
+
+  mDocFlags |= eScrollInitialized;
+  nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable();
+  if (sf) {
+    sf->AddScrollPositionListener(this);
+
+#ifdef A11Y_LOG
+    if (logging::IsEnabled(logging::eDocCreate))
+      logging::Text("add scroll listener");
+#endif
+  }
+}
+
+inline void
+DocAccessible::RemoveScrollListener()
+{
+  nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable();
+  if (sf)
+    sf->RemoveScrollPositionListener(this);
+}
+
+inline void
 DocAccessible::NotifyOfLoad(uint32_t aLoadEventType)
 {
   mLoadState |= eDOMLoaded;
   mLoadEventType = aLoadEventType;
 
   // If the document is loaded completely then network activity was presumingly
   // caused by file loading. Fire busy state change event.
   if (HasLoadState(eCompletelyLoaded) && IsLoadEventTarget()) {
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -70,17 +70,17 @@ static const uint32_t kRelationAttrsLen 
 ////////////////////////////////////////////////////////////////////////////////
 // Constructor/desctructor
 
 DocAccessible::
   DocAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
                   nsIPresShell* aPresShell) :
   HyperTextAccessibleWrap(aRootContent, this),
   mDocumentNode(aDocument), mScrollPositionChangedTicks(0),
-  mLoadState(eTreeConstructionPending), mLoadEventType(0),
+  mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
   mVirtualCursor(nullptr),
   mPresShell(aPresShell)
 {
   mGenericTypes |= eDocument;
   mStateFlags |= eNotNodeMapEntry;
 
   MOZ_ASSERT(mPresShell, "should have been given a pres shell");
   mPresShell->SetDocAccessible(this);
@@ -88,28 +88,16 @@ DocAccessible::
   mDependentIDsHash.Init();
   // XXX aaronl should we use an algorithm for the initial cache size?
   mAccessibleCache.Init(kDefaultCacheSize);
   mNodeToAccessibleMap.Init(kDefaultCacheSize);
 
   // If this is a XUL Document, it should not implement nsHyperText
   if (mDocumentNode && mDocumentNode->IsXUL())
     mGenericTypes &= ~eHyperText;
-
-  // For GTK+ native window, we do nothing here.
-  if (!mDocumentNode)
-    return;
-
-  // DocManager creates document accessible when scrollable frame is
-  // available already, it should be safe time to add scroll listener.
-  AddScrollListener();
-
-  // We provide a virtual cursor if this is a root doc or if it's a tab doc.
-  mIsCursorable = (!(mDocumentNode->GetParentDocument()) ||
-                   nsCoreUtils::IsTabDocument(mDocumentNode));
 }
 
 DocAccessible::~DocAccessible()
 {
   NS_ASSERTION(!mPresShell, "LastRelease was never called!?!");
 }
 
 
@@ -140,17 +128,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleCursorable,
-                                     mIsCursorable)
+                                     (mDocFlags & eCursorable))
     foundInterface = 0;
 
   nsresult status;
   if (!foundInterface) {
     // HTML document accessible must inherit from HyperTextAccessible to get
     // support text interfaces. XUL document accessible doesn't need this.
     // However at some point we may push <body> to implement the interfaces and
     // return DocAccessible to inherit from AccessibleWrap.
@@ -497,17 +485,18 @@ NS_IMETHODIMP
 DocAccessible::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor)
 {
   NS_ENSURE_ARG_POINTER(aVirtualCursor);
   *aVirtualCursor = nullptr;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
-  NS_ENSURE_TRUE(mIsCursorable, NS_ERROR_NOT_IMPLEMENTED);
+  if (!(mDocFlags & eCursorable))
+    return NS_OK;
 
   if (!mVirtualCursor) {
     mVirtualCursor = new nsAccessiblePivot(this);
     mVirtualCursor->AddObserver(this);
   }
 
   NS_ADDREF(*aVirtualCursor = mVirtualCursor);
   return NS_OK;
@@ -793,46 +782,16 @@ DocAccessible::ScrollTimerCallback(nsITi
     if (docAcc->mScrollWatchTimer) {
       docAcc->mScrollWatchTimer->Cancel();
       docAcc->mScrollWatchTimer = nullptr;
       NS_RELEASE(docAcc); // Release kung fu death grip
     }
   }
 }
 
-// DocAccessible protected member
-void
-DocAccessible::AddScrollListener()
-{
-  if (!mPresShell)
-    return;
-
-  nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollableExternal();
-  if (sf) {
-    sf->AddScrollPositionListener(this);
-#ifdef A11Y_LOG
-    if (logging::IsEnabled(logging::eDocCreate))
-      logging::Text("add scroll listener");
-#endif
-  }
-}
-
-// DocAccessible protected member
-void
-DocAccessible::RemoveScrollListener()
-{
-  if (!mPresShell)
-    return;
- 
-  nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollableExternal();
-  if (sf) {
-    sf->RemoveScrollPositionListener(this);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // nsIScrollPositionListener
 
 void
 DocAccessible::ScrollPositionDidChange(nscoord aX, nscoord aY)
 {
   // Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes,
   // then the ::Notify() method will fire the accessibility event for scroll position changes
@@ -1506,16 +1465,21 @@ DocAccessible::NotifyOfLoading(bool aIsR
   nsRefPtr<AccEvent> stateEvent =
     new AccStateChangeEvent(this, states::BUSY, true);
   FireDelayedEvent(stateEvent);
 }
 
 void
 DocAccessible::DoInitialUpdate()
 {
+  // We provide a virtual cursor if this is a root doc or if it's a tab doc.
+  if (!(mDocumentNode->GetParentDocument()) ||
+      nsCoreUtils::IsTabDocument(mDocumentNode))
+    mDocFlags |= eCursorable;
+
   mLoadState |= eTreeConstructed;
 
   // The content element may be changed before the initial update and then we
   // miss the notification (since content tree change notifications are ignored
   // prior to initial update). Make sure the content element is valid.
   nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocumentNode);
   if (mContent != contentElm) {
     mContent = contentElm;
--- a/accessible/src/generic/DocAccessible.h
+++ b/accessible/src/generic/DocAccessible.h
@@ -328,18 +328,21 @@ protected:
   virtual void DoInitialUpdate();
 
   /**
    * Process document load notification, fire document load and state busy
    * events if applicable.
    */
   void ProcessLoad();
 
-    void AddScrollListener();
-    void RemoveScrollListener();
+  /**
+   * Add/remove scroll listeners, @see nsIScrollPositionListener interface.
+   */
+  void AddScrollListener();
+  void RemoveScrollListener();
 
   /**
    * Append the given document accessible to this document's child document
    * accessibles.
    */
   bool AppendChildDocument(DocAccessible* aChildDocument)
   {
     return mChildDocuments.AppendElement(aChildDocument);
@@ -478,30 +481,46 @@ protected:
    * @param aTimer    [in] the timer object
    * @param aClosure  [in] the document accessible where scrolling happens
    */
   static void ScrollTimerCallback(nsITimer* aTimer, void* aClosure);
 
 protected:
 
   /**
+   * State and property flags, kept by mDocFlags.
+   */
+  enum {
+    // Whether scroll listeners were added.
+    eScrollInitialized = 1 << 0,
+
+    // Whether we support nsIAccessibleCursorable.
+    eCursorable = 1 << 1
+  };
+
+  /**
    * Cache of accessibles within this document accessible.
    */
   AccessibleHashtable mAccessibleCache;
   nsDataHashtable<nsPtrHashKey<const nsINode>, Accessible*>
     mNodeToAccessibleMap;
 
     nsCOMPtr<nsIDocument> mDocumentNode;
     nsCOMPtr<nsITimer> mScrollWatchTimer;
     uint16_t mScrollPositionChangedTicks; // Used for tracking scroll events
 
   /**
    * Bit mask of document load states (@see LoadState).
    */
-  uint32_t mLoadState;
+  uint32_t mLoadState : 3;
+
+  /**
+   * Bit mask of other states and props.
+   */
+  uint32_t mDocFlags : 28;
 
   /**
    * Type of document load event fired after the document is loaded completely.
    */
   uint32_t mLoadEventType;
 
   /**
    * Reference to anchor jump element.
@@ -512,21 +531,16 @@ protected:
    * Keep the ARIA attribute old value that is initialized by
    * AttributeWillChange and used by AttributeChanged notifications.
    */
   nsIAtom* mARIAAttrOldValue;
 
   nsTArray<nsRefPtr<DocAccessible> > mChildDocuments;
 
   /**
-   * Whether we support nsIAccessibleCursorable, used when querying the interface.
-   */
-  bool mIsCursorable;
-
-  /**
    * The virtual cursor of the document when it supports nsIAccessibleCursorable.
    */
   nsRefPtr<nsAccessiblePivot> mVirtualCursor;
 
   /**
    * A storage class for pairing content with one of its relation attributes.
    */
   class AttrRelProvider
--- a/accessible/tests/mochitest/states/test_frames.html
+++ b/accessible/tests/mochitest/states/test_frames.html
@@ -27,17 +27,16 @@
       testStates(frameDoc, STATE_READONLY, 0, 0, 0,
                  "test1: frameDoc");
       testStates(frameDocArticle, STATE_READONLY, 0, 0, 0,
                  "test1: frameDocArticle");
       testStates(frameDocCheckbox, 0, 0, STATE_READONLY, 0,
                  "test1: frameDocCheckbox");
       testStates(frameDocTextbox, 0, EXT_STATE_EDITABLE, STATE_READONLY, 0,
                  "test1: frameDocTextbox");
-
       frameDoc.designMode = "on";
       testStates(frameDoc,  0, EXT_STATE_EDITABLE, STATE_READONLY, 0,
                  "test2: frameDoc");
       testStates(frameDocArticle, STATE_READONLY, 0, 0, 0,
                  "test2: frameDocArticle");
       testStates(frameDocCheckbox, 0, 0, STATE_READONLY, 0,
                  "test2: frameDocCheckbox");
       testStates(frameDocTextbox, 0, EXT_STATE_EDITABLE, STATE_READONLY, 0,