Commit d320cbe9 authored by Yoshifumi Inoue's avatar Yoshifumi Inoue Committed by Commit Bot

Make DocumentMarkerController to detect empty marker set after processing weak member processing

This patch introduces weak member callback in |DocumentMarkerController| to
detect empty weak hash map |markers_| to avoid unregistering mutation observer
during mutation callback.

This patch is follow-up of the patch[1].

[1] http://crrev.com/c/1137988 Blink Editing: Workaround unsafe iteration in
DocumentMarkerController.

TBR=jbroman@chromium.org

Bug: 862960
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng
Change-Id: Icd6acfed2ca00dabc4ed79959df2fdf0bd6fce4b
Reviewed-on: https://chromium-review.googlesource.com/1143113Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKeishi Hattori <keishi@chromium.org>
Reviewed-by: default avatarVictor Costan <pwnall@chromium.org>
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577130}
parent 6f292316
...@@ -46,14 +46,7 @@ void SynchronousMutationNotifier::NotifyUpdateCharacterData( ...@@ -46,14 +46,7 @@ void SynchronousMutationNotifier::NotifyUpdateCharacterData(
unsigned offset, unsigned offset,
unsigned old_length, unsigned old_length,
unsigned new_length) { unsigned new_length) {
// Using ForEachObserverWithoutChecks() instead of ForEachObserver() is ForEachObserver([&](SynchronousMutationObserver* observer) {
// necessary because DocumentMarkerController::DidUpdateCharacterData ends up
// calling SynchronousMutationNotifier::RemoveObserver, which is unsafe and
// can result in memory corruption.
//
// TODO(crbug.com/862900): Fix DocumentMarkerController and switch to
// ForEachObsever() here.
ForEachObserverWithoutChecks([&](SynchronousMutationObserver* observer) {
observer->DidUpdateCharacterData(character_data, offset, old_length, observer->DidUpdateCharacterData(character_data, offset, old_length,
new_length); new_length);
}); });
......
...@@ -129,27 +129,14 @@ Member<DocumentMarkerList>& DocumentMarkerController::ListForType( ...@@ -129,27 +129,14 @@ Member<DocumentMarkerList>& DocumentMarkerController::ListForType(
} }
bool DocumentMarkerController::PossiblyHasMarkers( bool DocumentMarkerController::PossiblyHasMarkers(
DocumentMarker::MarkerType type) { DocumentMarker::MarkerType type) const {
return PossiblyHasMarkers(DocumentMarker::MarkerTypes(type)); return PossiblyHasMarkers(DocumentMarker::MarkerTypes(type));
} }
inline bool DocumentMarkerController::PossiblyHasMarkers( inline bool DocumentMarkerController::PossiblyHasMarkers(
DocumentMarker::MarkerTypes types) { DocumentMarker::MarkerTypes types) const {
if (markers_.IsEmpty()) { DCHECK(!markers_.IsEmpty() ||
// It's possible for markers_ to become empty through garbage collection if possibly_existing_marker_types_ == DocumentMarker::MarkerTypes(0));
// all its Nodes are GC'ed since we only hold weak references, in which case
// possibly_existing_marker_types_ isn't reset to 0 as it is in the other
// codepaths that remove from markers_. Therefore, we check for this case
// here.
// Alternatively, we could handle this case at the time the Node is GC'ed,
// but that operation is more performance-sensitive than anywhere
// PossiblyHasMarkers() is used.
possibly_existing_marker_types_ = DocumentMarker::MarkerTypes();
SetContext(nullptr);
return false;
}
return possibly_existing_marker_types_.Intersects(types); return possibly_existing_marker_types_.Intersects(types);
} }
...@@ -662,7 +649,17 @@ void DocumentMarkerController::InvalidateRectsForAllTextMatchMarkers() { ...@@ -662,7 +649,17 @@ void DocumentMarkerController::InvalidateRectsForAllTextMatchMarkers() {
} }
} }
void DocumentMarkerController::DidProcessMarkerMap(Visitor* visitor) {
if (markers_.IsEmpty())
Clear();
}
void DocumentMarkerController::Trace(blink::Visitor* visitor) { void DocumentMarkerController::Trace(blink::Visitor* visitor) {
// Note: To make |DidProcessMarkerMap()| called after weak members callback
// of |markers_|, we should register it before tracing |markers_|.
visitor->template RegisterWeakMembers<
DocumentMarkerController, &DocumentMarkerController::DidProcessMarkerMap>(
this);
visitor->Trace(markers_); visitor->Trace(markers_);
visitor->Trace(document_); visitor->Trace(document_);
SynchronousMutationObserver::Trace(visitor); SynchronousMutationObserver::Trace(visitor);
......
...@@ -152,8 +152,8 @@ class CORE_EXPORT DocumentMarkerController final ...@@ -152,8 +152,8 @@ class CORE_EXPORT DocumentMarkerController final
using MarkerMap = HeapHashMap<WeakMember<const Node>, Member<MarkerLists>>; using MarkerMap = HeapHashMap<WeakMember<const Node>, Member<MarkerLists>>;
static Member<DocumentMarkerList>& ListForType(MarkerLists*, static Member<DocumentMarkerList>& ListForType(MarkerLists*,
DocumentMarker::MarkerType); DocumentMarker::MarkerType);
bool PossiblyHasMarkers(DocumentMarker::MarkerTypes); bool PossiblyHasMarkers(DocumentMarker::MarkerTypes) const;
bool PossiblyHasMarkers(DocumentMarker::MarkerType); bool PossiblyHasMarkers(DocumentMarker::MarkerType) const;
void RemoveMarkersFromList(MarkerMap::iterator, DocumentMarker::MarkerTypes); void RemoveMarkersFromList(MarkerMap::iterator, DocumentMarker::MarkerTypes);
void RemoveMarkers(TextIterator&, DocumentMarker::MarkerTypes); void RemoveMarkers(TextIterator&, DocumentMarker::MarkerTypes);
void RemoveMarkersInternal(const Node&, void RemoveMarkersInternal(const Node&,
...@@ -161,6 +161,9 @@ class CORE_EXPORT DocumentMarkerController final ...@@ -161,6 +161,9 @@ class CORE_EXPORT DocumentMarkerController final
int length, int length,
DocumentMarker::MarkerTypes); DocumentMarker::MarkerTypes);
// Called after weak processing of |markers_| is done.
void DidProcessMarkerMap(Visitor* visitor);
MarkerMap markers_; MarkerMap markers_;
// Provide a quick way to determine whether a particular marker type is absent // Provide a quick way to determine whether a particular marker type is absent
// without going through the map. // without going through the map.
......
...@@ -189,10 +189,8 @@ TEST_F(DocumentMarkerControllerTest, NodeWillBeRemovedBySetInnerHTML) { ...@@ -189,10 +189,8 @@ TEST_F(DocumentMarkerControllerTest, NodeWillBeRemovedBySetInnerHTML) {
EXPECT_EQ(0u, MarkerController().Markers().size()); EXPECT_EQ(0u, MarkerController().Markers().size());
} }
// TODO(crbug.com/862900): Fix DocumentMarkerController::DidUpdateCharacterData // For http://crbug.com/862900
// and enable this test. TEST_F(DocumentMarkerControllerTest, SynchronousMutationNotificationAfterGC) {
TEST_F(DocumentMarkerControllerTest,
DISABLED_SynchronousMutationNotificationAfterGC) {
SetBodyContent("<b><i>foo</i></b>"); SetBodyContent("<b><i>foo</i></b>");
Persistent<Text> sibling_text = CreateTextNode("bar"); Persistent<Text> sibling_text = CreateTextNode("bar");
{ {
......
...@@ -81,20 +81,6 @@ class LifecycleNotifier : public GarbageCollectedMixin { ...@@ -81,20 +81,6 @@ class LifecycleNotifier : public GarbageCollectedMixin {
} }
} }
// ForEachObserver() variant that does not protect against memory corruption.
//
// Only used by SynchronousMutationNotifier::NotifyUpdateCharacterData. See
// implementation comment for why it is necessary.
//
// TODO(crbug.com/862900): Fix SynchronousMutationNotifier and remove this.
template <typename ForEachCallable>
void ForEachObserverWithoutChecks(const ForEachCallable& callable) const {
for (LifecycleObserverBase* observer_base : observers_) {
Observer* observer = static_cast<Observer*>(observer_base);
callable(observer);
}
}
private: private:
using ObserverSet = HeapHashSet<WeakMember<LifecycleObserverBase>>; using ObserverSet = HeapHashSet<WeakMember<LifecycleObserverBase>>;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment