| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/exported/web_form_element_observer_impl.h" |
| |
| #include "third_party/blink/public/web/web_form_control_element.h" |
| #include "third_party/blink/public/web/web_form_element.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_mutation_observer_init.h" |
| #include "third_party/blink/renderer/core/css/css_computed_style_declaration.h" |
| #include "third_party/blink/renderer/core/dom/mutation_observer.h" |
| #include "third_party/blink/renderer/core/dom/mutation_record.h" |
| #include "third_party/blink/renderer/core/dom/static_node_list.h" |
| #include "third_party/blink/renderer/core/html/forms/html_form_element.h" |
| #include "third_party/blink/renderer/core/html/html_element.h" |
| |
| namespace blink { |
| |
| class WebFormElementObserverImpl::ObserverCallback |
| : public MutationObserver::Delegate { |
| public: |
| ObserverCallback(HTMLElement&, base::OnceClosure callback); |
| |
| ExecutionContext* GetExecutionContext() const override; |
| |
| void Deliver(const MutationRecordVector& records, MutationObserver&) override; |
| |
| void Disconnect(); |
| |
| void Trace(Visitor*) override; |
| |
| private: |
| Member<HTMLElement> element_; |
| HeapHashSet<Member<Node>> parents_; |
| Member<MutationObserver> mutation_observer_; |
| base::OnceClosure callback_; |
| }; |
| |
| WebFormElementObserverImpl::ObserverCallback::ObserverCallback( |
| HTMLElement& element, |
| base::OnceClosure callback) |
| : element_(element), |
| mutation_observer_(MutationObserver::Create(this)), |
| callback_(std::move(callback)) { |
| { |
| MutationObserverInit* init = MutationObserverInit::Create(); |
| init->setAttributes(true); |
| init->setAttributeFilter({"class", "style"}); |
| mutation_observer_->observe(element_, init, ASSERT_NO_EXCEPTION); |
| } |
| for (Node* node = element_; node->parentElement(); |
| node = node->parentElement()) { |
| MutationObserverInit* init = MutationObserverInit::Create(); |
| init->setChildList(true); |
| init->setAttributes(true); |
| init->setAttributeFilter({"class", "style"}); |
| mutation_observer_->observe(node->parentElement(), init, |
| ASSERT_NO_EXCEPTION); |
| parents_.insert(node->parentElement()); |
| } |
| } |
| |
| ExecutionContext* |
| WebFormElementObserverImpl::ObserverCallback::GetExecutionContext() const { |
| return element_ ? element_->GetExecutionContext() : nullptr; |
| } |
| |
| void WebFormElementObserverImpl::ObserverCallback::Deliver( |
| const MutationRecordVector& records, |
| MutationObserver&) { |
| for (const auto& record : records) { |
| if (record->type() == "childList") { |
| for (unsigned i = 0; i < record->removedNodes()->length(); ++i) { |
| Node* removed_node = record->removedNodes()->item(i); |
| if (removed_node != element_ && !parents_.Contains(removed_node)) { |
| continue; |
| } |
| std::move(callback_).Run(); |
| Disconnect(); |
| return; |
| } |
| } else { |
| // Either "style" or "class" was modified. Check the computed style. |
| auto* style = |
| MakeGarbageCollected<CSSComputedStyleDeclaration>(record->target()); |
| if (style->GetPropertyValue(CSSPropertyID::kDisplay) == "none") { |
| std::move(callback_).Run(); |
| Disconnect(); |
| return; |
| } |
| } |
| } |
| } |
| |
| void WebFormElementObserverImpl::ObserverCallback::Disconnect() { |
| mutation_observer_->disconnect(); |
| callback_ = base::OnceClosure(); |
| } |
| |
| void WebFormElementObserverImpl::ObserverCallback::Trace( |
| blink::Visitor* visitor) { |
| visitor->Trace(element_); |
| visitor->Trace(parents_); |
| visitor->Trace(mutation_observer_); |
| MutationObserver::Delegate::Trace(visitor); |
| } |
| |
| WebFormElementObserver* WebFormElementObserver::Create( |
| WebFormElement& element, |
| base::OnceClosure callback) { |
| return MakeGarbageCollected<WebFormElementObserverImpl>( |
| util::PassKey<WebFormElementObserver>(), |
| *element.Unwrap<HTMLFormElement>(), std::move(callback)); |
| } |
| |
| WebFormElementObserver* WebFormElementObserver::Create( |
| WebFormControlElement& element, |
| base::OnceClosure callback) { |
| return MakeGarbageCollected<WebFormElementObserverImpl>( |
| util::PassKey<WebFormElementObserver>(), *element.Unwrap<HTMLElement>(), |
| std::move(callback)); |
| } |
| |
| WebFormElementObserverImpl::WebFormElementObserverImpl( |
| util::PassKey<WebFormElementObserver>, |
| HTMLElement& element, |
| base::OnceClosure callback) |
| : self_keep_alive_(PERSISTENT_FROM_HERE, this) { |
| mutation_callback_ = |
| MakeGarbageCollected<ObserverCallback>(element, std::move(callback)); |
| } |
| |
| WebFormElementObserverImpl::~WebFormElementObserverImpl() = default; |
| |
| void WebFormElementObserverImpl::Disconnect() { |
| mutation_callback_->Disconnect(); |
| mutation_callback_ = nullptr; |
| self_keep_alive_.Clear(); |
| } |
| |
| void WebFormElementObserverImpl::Trace(Visitor* visitor) { |
| visitor->Trace(mutation_callback_); |
| } |
| |
| } // namespace blink |