From db7566fedfb2bd52b3c64b075115069e974578f2 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Thu, 19 May 2016 14:35:20 -0400 Subject: [PATCH] Bug 1268320 - stop event tree processing if the document goes shutdown, r=yzen --- accessible/base/EventTree.cpp | 22 +++++-- accessible/base/EventTree.h | 2 +- accessible/base/NotificationController.cpp | 4 +- .../tests/mochitest/events/test_mutation.html | 61 ++++++++++++++++++- 4 files changed, 81 insertions(+), 8 deletions(-) diff --git a/accessible/base/EventTree.cpp b/accessible/base/EventTree.cpp index a194b8c10172..4dc609b0e032 100644 --- a/accessible/base/EventTree.cpp +++ b/accessible/base/EventTree.cpp @@ -181,12 +181,15 @@ TreeMutation::Done() // EventTree void -EventTree::Process() +EventTree::Process(const RefPtr& aDeathGrip) { while (mFirst) { // Skip a node and its subtree if its container is not in the document. if (mFirst->mContainer->IsInDocument()) { - mFirst->Process(); + mFirst->Process(aDeathGrip); + if (aDeathGrip->IsDefunct()) { + return; + } } mFirst = mFirst->mNext.forget(); } @@ -195,6 +198,7 @@ EventTree::Process() "No container, no events"); MOZ_ASSERT(!mContainer || !mContainer->IsDefunct(), "Processing events for defunct container"); + MOZ_ASSERT(!mFireReorder || mContainer, "No target for reorder event"); // Fire mutation events. uint32_t eventsCount = mDependentEvents.Length(); @@ -202,10 +206,18 @@ EventTree::Process() AccMutationEvent* mtEvent = mDependentEvents[jdx]; MOZ_ASSERT(mtEvent->mEventRule != AccEvent::eDoNotEmit, "The event shouldn't be presented in the tree"); + MOZ_ASSERT(mtEvent->Document(), "No document for event target"); nsEventShell::FireEvent(mtEvent); + if (aDeathGrip->IsDefunct()) { + return; + } + if (mtEvent->mTextChangeEvent) { nsEventShell::FireEvent(mtEvent->mTextChangeEvent); + if (aDeathGrip->IsDefunct()) { + return; + } } if (mtEvent->IsHide()) { @@ -221,18 +233,20 @@ EventTree::Process() if (mtEvent->mAccessible->ARIARole() == roles::MENUPOPUP) { nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, mtEvent->mAccessible); + if (aDeathGrip->IsDefunct()) { + return; + } } AccHideEvent* hideEvent = downcast_accEvent(mtEvent); if (hideEvent->NeedsShutdown()) { - mtEvent->Document()->ShutdownChildrenInSubtree(mtEvent->mAccessible); + aDeathGrip->ShutdownChildrenInSubtree(mtEvent->mAccessible); } } } // Fire reorder event at last. if (mFireReorder) { - MOZ_ASSERT(mContainer); nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer); mContainer->Document()->MaybeNotifyOfValueChange(mContainer); } diff --git a/accessible/base/EventTree.h b/accessible/base/EventTree.h index c52684f228be..e2d5ef24bc5b 100644 --- a/accessible/base/EventTree.h +++ b/accessible/base/EventTree.h @@ -87,7 +87,7 @@ private: /** * Processes the event queue and fires events. */ - void Process(); + void Process(const RefPtr& aDeathGrip); /** * Return an event subtree for the given accessible. diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp index a0ad0bfa0248..3e75c1ec2b70 100644 --- a/accessible/base/NotificationController.cpp +++ b/accessible/base/NotificationController.cpp @@ -401,7 +401,9 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime) // events causes script to run. mObservingState = eRefreshProcessing; - mEventTree.Process(); + RefPtr deathGrip(mDocument); + mEventTree.Process(deathGrip); + deathGrip = nullptr; ProcessEventQueue(); diff --git a/accessible/tests/mochitest/events/test_mutation.html b/accessible/tests/mochitest/events/test_mutation.html index b05ac00883f4..337f9854ef8c 100644 --- a/accessible/tests/mochitest/events/test_mutation.html +++ b/accessible/tests/mochitest/events/test_mutation.html @@ -377,6 +377,58 @@ } } + function hideNDestroyDoc() + { + this.txt = null; + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, function() { return this.txt; }.bind(this)) + ]; + + this.invoke = function hideNDestroyDoc_invoke() + { + this.txt = getAccessible('c5').firstChild.firstChild; + this.txt.DOMNode.parentNode.removeChild(this.txt.DOMNode); + } + + this.check = function hideNDestroyDoc_check() + { + getNode('c5').parentNode.removeChild(getNode('c5')); + } + + this.getID = function hideNDestroyDoc_getID() + { + return "remove text node and destroy a document on hide event"; + } + } + + function hideHideNDestroyDoc() + { + this.target = null; + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, function() { return this.target; }.bind(this)) + ]; + + this.invoke = function hideHideNDestroyDoc_invoke() + { + var doc = getAccessible('c6').firstChild; + var l1 = doc.firstChild; + this.target = l1.firstChild; + var l2 = doc.lastChild; + l1.DOMNode.removeChild(l1.DOMNode.firstChild); + l2.DOMNode.removeChild(l2.DOMNode.firstChild); + } + + this.check = function hideHideNDestroyDoc_check() + { + getNode('c6').parentNode.removeChild(getNode('c6')); + } + + this.getID = function hideHideNDestroyDoc_getID() + { + return "remove text nodes (2 events in the queue) and destroy a document on first hide event"; + } + } + /** * Target getters. */ @@ -507,6 +559,8 @@ gQueue.push(new insertReferredElm("testContainer3")); gQueue.push(new showHiddenParentOfVisibleChild()); + gQueue.push(new hideNDestroyDoc()); + gQueue.push(new hideHideNDestroyDoc()); gQueue.invoke(); // Will call SimpleTest.finish(); } @@ -569,7 +623,10 @@
+
+
+ + +