blob: 232622c41e71dc96e88a6c8b878222467ad52a7f [file] [log] [blame]
// Copyright 2014 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 "config.h"
#include "core/dom/EventHandlerRegistry.h"
#include "core/dom/Document.h"
#include "core/events/ThreadLocalEventNames.h"
#include "core/events/WheelEvent.h"
#include "core/frame/FrameHost.h"
#include "core/frame/LocalFrame.h"
#include "core/page/Chrome.h"
#include "core/page/ChromeClient.h"
#include "core/page/Page.h"
#include "core/page/scrolling/ScrollingCoordinator.h"
namespace WebCore {
EventHandlerRegistry::HandlerState::HandlerState()
{
}
EventHandlerRegistry::HandlerState::~HandlerState()
{
}
EventHandlerRegistry::EventHandlerRegistry(Document& document)
: m_document(document)
{
}
EventHandlerRegistry::~EventHandlerRegistry()
{
}
const char* EventHandlerRegistry::supplementName()
{
return "EventHandlerRegistry";
}
EventHandlerRegistry* EventHandlerRegistry::from(Document& document)
{
EventHandlerRegistry* registry = static_cast<EventHandlerRegistry*>(DocumentSupplement::from(document, supplementName()));
if (!registry) {
registry = new EventHandlerRegistry(document);
DocumentSupplement::provideTo(document, supplementName(), adoptPtrWillBeNoop(registry));
}
return registry;
}
bool EventHandlerRegistry::eventTypeToClass(const AtomicString& eventType, EventHandlerClass* result)
{
if (eventType == EventTypeNames::scroll) {
*result = ScrollEvent;
} else {
return false;
}
return true;
}
const EventTargetSet* EventHandlerRegistry::eventHandlerTargets(EventHandlerClass handlerClass) const
{
return m_eventHandlers[handlerClass].targets.get();
}
bool EventHandlerRegistry::hasEventHandlers(EventHandlerClass handlerClass) const
{
EventTargetSet* targets = m_eventHandlers[handlerClass].targets.get();
return targets && targets->size();
}
bool EventHandlerRegistry::updateEventHandlerTargets(ChangeOperation op, EventHandlerClass handlerClass, EventTarget* target)
{
EventTargetSet* targets = m_eventHandlers[handlerClass].targets.get();
if (op == Add) {
#if ASSERT_ENABLED
if (Node* node = target->toNode())
ASSERT(&node->document() == &m_document);
#endif // ASSERT_ENABLED
if (!targets) {
m_eventHandlers[handlerClass].targets = adoptPtr(new EventTargetSet);
targets = m_eventHandlers[handlerClass].targets.get();
}
if (!targets->add(target).isNewEntry) {
// Just incremented refcount, no real change.
return false;
}
} else {
// Note that we can't assert that |target| is in this document because
// it might be in the process of moving out of it.
ASSERT(op == Remove || op == RemoveAll);
ASSERT(op == RemoveAll || targets->contains(target));
if (!targets)
return false;
if (op == RemoveAll) {
if (!targets->contains(target))
return false;
targets->removeAll(target);
} else {
if (!targets->remove(target)) {
// Just decremented refcount, no real update.
return false;
}
}
}
return true;
}
void EventHandlerRegistry::updateEventHandlerInternal(ChangeOperation op, EventHandlerClass handlerClass, EventTarget* target)
{
// After the document has stopped, all updates become no-ops.
if (!m_document.isActive()) {
return;
}
bool hadHandlers = hasEventHandlers(handlerClass);
updateEventHandlerTargets(op, handlerClass, target);
bool hasHandlers = hasEventHandlers(handlerClass);
// Notify the parent document's registry if we added the first or removed
// the last handler.
if (hadHandlers != hasHandlers && !m_document.parentDocument()) {
// This is the root registry; notify clients accordingly.
notifyHasHandlersChanged(handlerClass, hasHandlers);
}
}
void EventHandlerRegistry::updateEventHandlerOfType(ChangeOperation op, const AtomicString& eventType, EventTarget* target)
{
EventHandlerClass handlerClass;
if (!eventTypeToClass(eventType, &handlerClass))
return;
updateEventHandlerInternal(op, handlerClass, target);
}
void EventHandlerRegistry::didAddEventHandler(EventTarget& target, const AtomicString& eventType)
{
updateEventHandlerOfType(Add, eventType, &target);
}
void EventHandlerRegistry::didRemoveEventHandler(EventTarget& target, const AtomicString& eventType)
{
updateEventHandlerOfType(Remove, eventType, &target);
}
void EventHandlerRegistry::didAddEventHandler(EventTarget& target, EventHandlerClass handlerClass)
{
updateEventHandlerInternal(Add, handlerClass, &target);
}
void EventHandlerRegistry::didRemoveEventHandler(EventTarget& target, EventHandlerClass handlerClass)
{
updateEventHandlerInternal(Remove, handlerClass, &target);
}
void EventHandlerRegistry::didMoveFromOtherDocument(EventTarget& target, Document& oldDocument)
{
EventHandlerRegistry* oldRegistry = EventHandlerRegistry::from(oldDocument);
for (size_t i = 0; i < EventHandlerClassCount; ++i) {
EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
const EventTargetSet* targets = oldRegistry->eventHandlerTargets(handlerClass);
if (!targets)
continue;
for (unsigned count = targets->count(&target); count > 0; --count) {
oldRegistry->updateEventHandlerInternal(Remove, handlerClass, &target);
updateEventHandlerInternal(Add, handlerClass, &target);
}
}
}
void EventHandlerRegistry::didRemoveAllEventHandlers(EventTarget& target)
{
for (size_t i = 0; i < EventHandlerClassCount; ++i) {
EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
const EventTargetSet* targets = eventHandlerTargets(handlerClass);
if (!targets)
continue;
updateEventHandlerInternal(RemoveAll, handlerClass, &target);
}
}
void EventHandlerRegistry::notifyHasHandlersChanged(EventHandlerClass handlerClass, bool hasActiveHandlers)
{
Page* page = m_document.page();
ScrollingCoordinator* scrollingCoordinator = page ? page->scrollingCoordinator() : 0;
switch (handlerClass) {
case ScrollEvent:
if (scrollingCoordinator)
scrollingCoordinator->updateHaveScrollEventHandlers();
break;
default:
ASSERT_NOT_REACHED();
break;
}
}
void EventHandlerRegistry::trace(Visitor* visitor)
{
visitor->registerWeakMembers<EventHandlerRegistry, &EventHandlerRegistry::clearWeakMembers>(this);
}
void EventHandlerRegistry::clearWeakMembers(Visitor* visitor)
{
// FIXME: Oilpan: This is pretty funky. The current code disables all modifications of the
// EventHandlerRegistry when the document becomes inactive. To keep that behavior we only
// perform weak processing of the registry when the document is active.
if (!m_document.isActive())
return;
Vector<EventTarget*> deadNodeTargets;
for (size_t i = 0; i < EventHandlerClassCount; ++i) {
EventHandlerClass handlerClass = static_cast<EventHandlerClass>(i);
const EventTargetSet* targets = eventHandlerTargets(handlerClass);
if (!targets)
continue;
for (EventTargetSet::const_iterator it = targets->begin(); it != targets->end(); ++it) {
Node* node = it->key->toNode();
if (node && !visitor->isAlive(node))
deadNodeTargets.append(node);
}
}
for (size_t i = 0; i < deadNodeTargets.size(); ++i)
didRemoveAllEventHandlers(*deadNodeTargets[i]);
}
} // namespace WebCore