blob: 49bb1a17a4beecc587fc0548bd20ca5db1a95418 [file] [log] [blame]
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/dom/registered_observer_list.h"
namespace cobalt {
namespace dom {
namespace {
// This algorithm is part of the description of the observe() method. It may
// modify |options| and returns false if a TypeError should be thrown.
// https://www.w3.org/TR/dom/#dom-mutationobserver-observe
bool InitializeOptions(MutationObserverInit* options) {
if (options->has_attribute_old_value() || options->has_attribute_filter()) {
// 1. If either options' attributeOldValue or attributeFilter is present and
// options' attributes is omitted, set options' attributes to true.
if (!options->has_attributes()) {
options->set_attributes(true);
}
// 4. If options' attributeOldValue is true and options' attributes is
// false, throw a JavaScript TypeError.
// 5. If options' attributeFilter is present and options' attributes is
// false, throw a JavaScript TypeError.
if (!options->attributes()) {
return false;
}
}
if (options->has_character_data_old_value()) {
// 2. If options' characterDataOldValue is present and options'
// characterData is omitted, set options' characterData to true.
if (!options->has_character_data()) {
options->set_character_data(true);
}
// 6. If options' characterDataOldValue is true and options' characterData
// is false, throw a JavaScript TypeError.
if (!options->character_data()) {
return false;
}
}
// 3. If none of options' childList attributes, and characterData is true,
// throw a TypeError.
const bool child_list = options->child_list();
const bool attributes = options->has_attributes() && options->attributes();
const bool character_data =
options->has_character_data() && options->character_data();
if (!(child_list || attributes || character_data)) {
return false;
}
return true;
}
} // namespace
bool RegisteredObserverList::AddMutationObserver(
const scoped_refptr<MutationObserver>& observer,
const MutationObserverInit& const_options) {
MutationObserverInit options(const_options);
if (!InitializeOptions(&options)) {
return false;
}
// https://www.w3.org/TR/dom/#dom-mutationobserver-observe
// 7. For each registered observer registered in target's list of registered
// observers whose observer is the context object:
// 1. Remove all transient registered observers whose source is
// registered.
// 2. Replace registered's options with options.
typedef RegisteredObserverVector::iterator RegisteredObserverIterator;
for (RegisteredObserverIterator it = registered_observers_.begin();
it != registered_observers_.end(); ++it) {
// TODO: Remove transient registered observers.
if (it->observer() == observer) {
it->set_options(options);
return true;
}
}
// 8. Otherwise, add a new registered observer to target's list of registered
// observers with the context object as the observer and options as the
// options, and add target to context object's list of nodes on which it is
// registered.
registered_observers_.push_back(
RegisteredObserver(target_, observer, options));
return true;
}
void RegisteredObserverList::RemoveMutationObserver(
const scoped_refptr<MutationObserver>& observer) {
typedef RegisteredObserverVector::iterator RegisteredObserverIterator;
for (RegisteredObserverIterator it = registered_observers_.begin();
it != registered_observers_.end(); ++it) {
if (it->observer() == observer) {
registered_observers_.erase(it);
return;
}
}
NOTREACHED() << "Did not find a mutation observer to unregister.";
}
} // namespace dom
} // namespace cobalt