blob: 0a17d3656ef868e070713f820ff78b330f488f74 [file] [log] [blame]
// Copyright 2021 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/html/custom/custom_state_set.h"
#include "third_party/blink/renderer/core/css/css_selector.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_idioms.h"
#include "third_party/blink/renderer/core/dom/element.h"
namespace blink {
CustomStateSet::CustomStateSet(Element& element) : element_(element) {}
void CustomStateSet::Trace(Visitor* visitor) const {
visitor->Trace(element_);
ScriptWrappable::Trace(visitor);
}
void CustomStateSet::add(const String& value, ExceptionState& exception_state) {
// https://wicg.github.io/custom-state-pseudo-class/#dom-customstateset-add
// 1. If value does not match to <dashed-ident>, then throw a "SyntaxError"
// DOMException.
if (!value.StartsWith("--")) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"The specified value '" + value + "' must start with '--'.");
return;
}
for (wtf_size_t i = 2; i < value.length(); ++i) {
if (IsNameCodePoint(value[i]))
continue;
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"The specified value '" + value +
"' must match to <dashed-ident> production. '" + value[i] +
"' is invalid.");
return;
}
// 2. Invoke the default add operation, which the setlike<DOMString> would
// have if CustomStateSet interface had no add(value) operation, with value
// argument.
set_.insert(value);
InvalidateStyle();
}
uint32_t CustomStateSet::size() const {
return set_.size();
}
void CustomStateSet::clearForBinding(ScriptState*, ExceptionState&) {
set_.clear();
InvalidateStyle();
}
bool CustomStateSet::deleteForBinding(ScriptState*,
const String& value,
ExceptionState&) {
auto iter = set_.find(value);
if (iter == set_.cend())
return false;
set_.erase(iter);
InvalidateStyle();
return true;
}
bool CustomStateSet::hasForBinding(ScriptState*,
const String& value,
ExceptionState&) const {
return Has(value);
}
bool CustomStateSet::Has(const String& value) const {
return set_.Contains(value);
}
class CustomStateIterationSource : public CustomStateSet::IterationSource {
public:
explicit CustomStateIterationSource(CustomStateSet& states)
: states_(states), iterator_(states.set_.begin()) {}
void Trace(Visitor* visitor) const override {
visitor->Trace(states_);
CustomStateSet::IterationSource::Trace(visitor);
}
bool Next(ScriptState*,
String& out_key,
String& out_value,
ExceptionState&) override {
if (iterator_ == states_->set_.end())
return false;
String value = *iterator_;
++iterator_;
out_key = value;
out_value = value;
return true;
}
private:
Member<CustomStateSet> states_;
LinkedHashSet<String>::const_iterator iterator_;
};
CustomStateSet::IterationSource* CustomStateSet::StartIteration(
ScriptState*,
ExceptionState&) {
return MakeGarbageCollected<CustomStateIterationSource>(*this);
}
void CustomStateSet::InvalidateStyle() const {
// TOOD(tkent): The following line invalidates all of rulesets with any
// custom state pseudo classes though we should invalidate only rulesets
// with the updated state ideally. We can improve style resolution
// performance in documents with various custom state pseudo classes by
// having blink::InvalidationSet for each of states.
element_->PseudoStateChanged(CSSSelector::kPseudoState);
}
} // namespace blink