# HG changeset patch # User Tobias Schneider # Date 1481252047 28800 # Thu Dec 08 18:54:07 2016 -0800 # Node ID d0925ebc02b4d3812cc16862ebf4e3181a24e6d3 # Parent d8f63b2935af0915a6a24b3ea8e27d9a09f66416 Bug 1317415 - Crash in PLDHashTable::Search | mozilla::dom::DOMIntersectionObserver::UnlinkTarget diff --git a/dom/base/DOMIntersectionObserver.cpp b/dom/base/DOMIntersectionObserver.cpp --- a/dom/base/DOMIntersectionObserver.cpp +++ b/dom/base/DOMIntersectionObserver.cpp @@ -47,6 +47,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries) + tmp->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver) @@ -185,9 +186,10 @@ if (mConnected) { return; } + + mConnected = true; nsIDocument* document = mOwner->GetExtantDoc(); document->AddIntersectionObserver(this); - mConnected = true; } void diff --git a/dom/base/DOMIntersectionObserver.h b/dom/base/DOMIntersectionObserver.h --- a/dom/base/DOMIntersectionObserver.h +++ b/dom/base/DOMIntersectionObserver.h @@ -101,9 +101,7 @@ class DOMIntersectionObserver final : public nsISupports, public nsWrapperCache { - virtual ~DOMIntersectionObserver() { - Disconnect(); - } + virtual ~DOMIntersectionObserver() { } public: DOMIntersectionObserver(already_AddRefed&& aOwner, diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -3869,7 +3869,7 @@ slots->mDataset = nullptr; } -nsTArray* +nsDataHashtable, int32_t>* Element::RegisteredIntersectionObservers() { nsDOMSlots* slots = DOMSlots(); @@ -3879,34 +3879,34 @@ void Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver) { - RegisteredIntersectionObservers()->AppendElement( - nsDOMSlots::IntersectionObserverRegistration { aObserver, -1 }); + nsDataHashtable, int32_t>* observers = + RegisteredIntersectionObservers(); + if (observers->Contains(aObserver)) { + return; + } + RegisteredIntersectionObservers()->Put(aObserver, -1); } void Element::UnregisterIntersectionObserver(DOMIntersectionObserver* aObserver) { - nsTArray* observers = + nsDataHashtable, int32_t>* observers = RegisteredIntersectionObservers(); - for (uint32_t i = 0; i < observers->Length(); ++i) { - nsDOMSlots::IntersectionObserverRegistration reg = observers->ElementAt(i); - if (reg.observer == aObserver) { - observers->RemoveElementAt(i); - break; - } - } + observers->Remove(aObserver); } bool Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver, int32_t aThreshold) { - nsTArray* observers = + nsDataHashtable, int32_t>* observers = RegisteredIntersectionObservers(); - for (auto& reg : *observers) { - if (reg.observer == aObserver && reg.previousThreshold != aThreshold) { - reg.previousThreshold = aThreshold; - return true; - } + if (!observers->Contains(aObserver)) { + return false; + } + int32_t previousThreshold = observers->Get(aObserver); + if (previousThreshold != aThreshold) { + observers->Put(aObserver, aThreshold); + return true; } return false; } diff --git a/dom/base/Element.h b/dom/base/Element.h --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -1423,7 +1423,7 @@ nsDOMTokenList* GetTokenList(nsIAtom* aAtom, const DOMTokenListSupportedTokenArray aSupportedTokens = nullptr); - nsTArray* RegisteredIntersectionObservers(); + nsDataHashtable, int32_t>* RegisteredIntersectionObservers(); private: /** diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -21,6 +21,7 @@ #include "nsIWeakReference.h" // base class #include "nsNodeUtils.h" // class member nsNodeUtils::CloneNodeImpl #include "nsIHTMLCollection.h" +#include "nsDataHashtable.h" class ContentUnbinder; class nsContentList; @@ -347,12 +348,7 @@ /** * Registered Intersection Observers on the element. */ - struct IntersectionObserverRegistration { - DOMIntersectionObserver* observer; - int32_t previousThreshold; - }; - - nsTArray mRegisteredIntersectionObservers; + nsDataHashtable, int32_t> mRegisteredIntersectionObservers; }; protected: diff --git a/dom/base/nsNodeUtils.cpp b/dom/base/nsNodeUtils.cpp --- a/dom/base/nsNodeUtils.cpp +++ b/dom/base/nsNodeUtils.cpp @@ -301,8 +301,9 @@ Element* elem = aNode->AsElement(); FragmentOrElement::nsDOMSlots* domSlots = static_cast(slots); - for (auto& reg : domSlots->mRegisteredIntersectionObservers) { - reg.observer->UnlinkTarget(*elem); + for (auto iter = domSlots->mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) { + DOMIntersectionObserver* observer = iter.Key(); + observer->UnlinkTarget(*elem); } } diff --git a/dom/base/test/test_intersectionobservers.html b/dom/base/test/test_intersectionobservers.html --- a/dom/base/test/test_intersectionobservers.html +++ b/dom/base/test/test_intersectionobservers.html @@ -891,6 +891,19 @@ var win = window.open("intersectionobserver_window.html"); }); + it('triggers only once if observed multiple times (and does not crash when collected)', function(done) { + var spy = sinon.spy(); + io = new IntersectionObserver(spy, {root: rootEl}); + io.observe(targetEl1); + io.observe(targetEl1); + io.observe(targetEl1); + + callDelayed(function () { + expect(spy.callCount).to.be(1); + done(); + }, ASYNC_TIMEOUT); + }); + }); describe('observe subframe', function () {