blob: 2bfc9e2cdef391f8e41e25263a5560bcc3a6ac27 [file] [log] [blame]
// 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 "web/WebFormElementObserverImpl.h"
#include "core/css/CSSComputedStyleDeclaration.h"
#include "core/dom/MutationCallback.h"
#include "core/dom/MutationObserver.h"
#include "core/dom/MutationObserverInit.h"
#include "core/dom/MutationRecord.h"
#include "core/dom/StaticNodeList.h"
#include "core/html/HTMLElement.h"
#include "core/html/HTMLFormElement.h"
#include "core/html/HTMLInputElement.h"
#include "public/web/WebFormElement.h"
#include "public/web/WebInputElement.h"
#include "public/web/modules/password_manager/WebFormElementObserverCallback.h"
namespace blink {
class WebFormElementObserverImpl::ObserverCallback : public MutationCallback {
public:
ObserverCallback(HTMLElement&,
std::unique_ptr<WebFormElementObserverCallback>);
DECLARE_VIRTUAL_TRACE();
ExecutionContext* GetExecutionContext() const override;
void Disconnect();
private:
void Call(const HeapVector<Member<MutationRecord>>& records,
MutationObserver*) override;
Member<HTMLElement> element_;
Member<MutationObserver> mutation_observer_;
std::unique_ptr<WebFormElementObserverCallback> callback_;
};
WebFormElementObserverImpl::ObserverCallback::ObserverCallback(
HTMLElement& element,
std::unique_ptr<WebFormElementObserverCallback> callback)
: element_(&element), callback_(std::move(callback)) {
DCHECK(element.ownerDocument());
mutation_observer_ = MutationObserver::Create(this);
{
Vector<String> filter;
filter.ReserveCapacity(3);
filter.push_back(String("action"));
filter.push_back(String("class"));
filter.push_back(String("style"));
MutationObserverInit init;
init.setAttributes(true);
init.setAttributeFilter(filter);
mutation_observer_->observe(element_, init, ASSERT_NO_EXCEPTION);
}
if (element_->parentElement()) {
MutationObserverInit init;
init.setChildList(true);
mutation_observer_->observe(element_->parentElement(), init,
ASSERT_NO_EXCEPTION);
}
}
ExecutionContext*
WebFormElementObserverImpl::ObserverCallback::GetExecutionContext() const {
return element_->ownerDocument();
}
void WebFormElementObserverImpl::ObserverCallback::Disconnect() {
mutation_observer_->disconnect();
callback_.reset();
}
void WebFormElementObserverImpl::ObserverCallback::Call(
const HeapVector<Member<MutationRecord>>& records,
MutationObserver*) {
for (const auto& record : records) {
if (record->type() == "childList") {
for (unsigned i = 0; i < record->removedNodes()->length(); ++i) {
if (record->removedNodes()->item(i) != element_)
continue;
callback_->ElementWasHiddenOrRemoved();
Disconnect();
return;
}
} else {
HTMLElement& element = *ToHTMLElement(record->target());
if (record->attributeName() == "action") {
// If the action was modified, we just assume that the form as
// submitted.
callback_->ElementWasHiddenOrRemoved();
Disconnect();
return;
}
// Otherwise, either "style" or "class" was modified. Check the
// computed style.
CSSComputedStyleDeclaration* style =
CSSComputedStyleDeclaration::Create(&element);
if (style->GetPropertyValue(CSSPropertyDisplay) == "none") {
callback_->ElementWasHiddenOrRemoved();
Disconnect();
return;
}
}
}
}
DEFINE_TRACE(WebFormElementObserverImpl::ObserverCallback) {
visitor->Trace(element_);
visitor->Trace(mutation_observer_);
MutationCallback::Trace(visitor);
}
WebFormElementObserver* WebFormElementObserver::Create(
WebFormElement& element,
std::unique_ptr<WebFormElementObserverCallback> callback) {
return new WebFormElementObserverImpl(*element.Unwrap<HTMLFormElement>(),
std::move(callback));
}
WebFormElementObserver* WebFormElementObserver::Create(
WebInputElement& element,
std::unique_ptr<WebFormElementObserverCallback> callback) {
return new WebFormElementObserverImpl(*element.Unwrap<HTMLInputElement>(),
std::move(callback));
}
WebFormElementObserverImpl::WebFormElementObserverImpl(
HTMLElement& element,
std::unique_ptr<WebFormElementObserverCallback> callback)
: self_keep_alive_(this) {
mutation_callback_ = new ObserverCallback(element, std::move(callback));
}
WebFormElementObserverImpl::~WebFormElementObserverImpl() {}
void WebFormElementObserverImpl::Disconnect() {
mutation_callback_->Disconnect();
mutation_callback_ = nullptr;
self_keep_alive_.Clear();
}
DEFINE_TRACE(WebFormElementObserverImpl) {
visitor->Trace(mutation_callback_);
}
} // namespace blink