| /* |
| * (C) 1999-2003 Lars Knoll (knoll@kde.org) |
| * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All |
| * rights reserved. |
| * Copyright (C) 2011 Research In Motion Limited. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "core/css/PropertySetCSSStyleDeclaration.h" |
| |
| #include "bindings/core/v8/ExceptionState.h" |
| #include "core/StylePropertyShorthand.h" |
| #include "core/css/CSSCustomPropertyDeclaration.h" |
| #include "core/css/CSSKeyframesRule.h" |
| #include "core/css/CSSPropertyValueSet.h" |
| #include "core/css/CSSStyleSheet.h" |
| #include "core/css/StyleChangeReason.h" |
| #include "core/css/StyleEngine.h" |
| #include "core/dom/Element.h" |
| #include "core/dom/MutationObserverInterestGroup.h" |
| #include "core/dom/MutationRecord.h" |
| #include "core/html/custom/CustomElement.h" |
| #include "core/html/custom/CustomElementDefinition.h" |
| #include "core/html_names.h" |
| #include "core/probe/CoreProbes.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| static CustomElementDefinition* DefinitionIfStyleChangedCallback( |
| Element* element) { |
| CustomElementDefinition* definition = |
| CustomElement::DefinitionForElement(element); |
| return definition && definition->HasStyleAttributeChangedCallback() |
| ? definition |
| : nullptr; |
| } |
| |
| class StyleAttributeMutationScope { |
| WTF_MAKE_NONCOPYABLE(StyleAttributeMutationScope); |
| STACK_ALLOCATED(); |
| |
| public: |
| DISABLE_CFI_PERF |
| StyleAttributeMutationScope(AbstractPropertySetCSSStyleDeclaration* decl) { |
| ++scope_count_; |
| |
| if (scope_count_ != 1) { |
| DCHECK_EQ(current_decl_, decl); |
| return; |
| } |
| |
| DCHECK(!current_decl_); |
| current_decl_ = decl; |
| |
| if (!current_decl_->ParentElement()) |
| return; |
| |
| mutation_recipients_ = |
| MutationObserverInterestGroup::CreateForAttributesMutation( |
| *current_decl_->ParentElement(), HTMLNames::styleAttr); |
| bool should_read_old_value = |
| (mutation_recipients_ && mutation_recipients_->IsOldValueRequested()) || |
| DefinitionIfStyleChangedCallback(current_decl_->ParentElement()); |
| |
| if (should_read_old_value) |
| old_value_ = |
| current_decl_->ParentElement()->getAttribute(HTMLNames::styleAttr); |
| |
| if (mutation_recipients_) { |
| AtomicString requested_old_value = |
| mutation_recipients_->IsOldValueRequested() ? old_value_ |
| : g_null_atom; |
| mutation_ = MutationRecord::CreateAttributes( |
| current_decl_->ParentElement(), HTMLNames::styleAttr, |
| requested_old_value); |
| } |
| } |
| |
| DISABLE_CFI_PERF |
| ~StyleAttributeMutationScope() { |
| --scope_count_; |
| if (scope_count_) |
| return; |
| |
| if (should_deliver_) { |
| if (mutation_) |
| mutation_recipients_->EnqueueMutationRecord(mutation_); |
| |
| Element* element = current_decl_->ParentElement(); |
| if (CustomElementDefinition* definition = |
| DefinitionIfStyleChangedCallback(element)) { |
| definition->EnqueueAttributeChangedCallback( |
| element, HTMLNames::styleAttr, old_value_, |
| element->getAttribute(HTMLNames::styleAttr)); |
| } |
| |
| should_deliver_ = false; |
| } |
| |
| // We have to clear internal state before calling Inspector's code. |
| AbstractPropertySetCSSStyleDeclaration* local_copy_style_decl = |
| current_decl_; |
| current_decl_ = nullptr; |
| |
| if (!should_notify_inspector_) |
| return; |
| |
| should_notify_inspector_ = false; |
| if (local_copy_style_decl->ParentElement()) |
| probe::didInvalidateStyleAttr(local_copy_style_decl->ParentElement()); |
| } |
| |
| void EnqueueMutationRecord() { should_deliver_ = true; } |
| |
| void DidInvalidateStyleAttr() { should_notify_inspector_ = true; } |
| |
| private: |
| static unsigned scope_count_; |
| static AbstractPropertySetCSSStyleDeclaration* current_decl_; |
| static bool should_notify_inspector_; |
| static bool should_deliver_; |
| |
| Member<MutationObserverInterestGroup> mutation_recipients_; |
| Member<MutationRecord> mutation_; |
| AtomicString old_value_; |
| }; |
| |
| unsigned StyleAttributeMutationScope::scope_count_ = 0; |
| AbstractPropertySetCSSStyleDeclaration* |
| StyleAttributeMutationScope::current_decl_ = nullptr; |
| bool StyleAttributeMutationScope::should_notify_inspector_ = false; |
| bool StyleAttributeMutationScope::should_deliver_ = false; |
| |
| } // namespace |
| |
| void PropertySetCSSStyleDeclaration::Trace(blink::Visitor* visitor) { |
| visitor->Trace(property_set_); |
| AbstractPropertySetCSSStyleDeclaration::Trace(visitor); |
| } |
| |
| unsigned AbstractPropertySetCSSStyleDeclaration::length() const { |
| return PropertySet().PropertyCount(); |
| } |
| |
| String AbstractPropertySetCSSStyleDeclaration::item(unsigned i) const { |
| if (i >= PropertySet().PropertyCount()) |
| return ""; |
| CSSPropertyValueSet::PropertyReference property = PropertySet().PropertyAt(i); |
| if (property.Id() == CSSPropertyVariable) |
| return ToCSSCustomPropertyDeclaration(property.Value()).GetName(); |
| return getPropertyName(property.Id()); |
| } |
| |
| String AbstractPropertySetCSSStyleDeclaration::cssText() const { |
| return PropertySet().AsText(); |
| } |
| |
| void AbstractPropertySetCSSStyleDeclaration::setCSSText( |
| const ExecutionContext* execution_context, |
| const String& text, |
| ExceptionState&) { |
| StyleAttributeMutationScope mutation_scope(this); |
| WillMutate(); |
| |
| PropertySet().ParseDeclarationList( |
| text, execution_context->SecureContextMode(), ContextStyleSheet()); |
| |
| DidMutate(kPropertyChanged); |
| |
| mutation_scope.EnqueueMutationRecord(); |
| } |
| |
| String AbstractPropertySetCSSStyleDeclaration::getPropertyValue( |
| const String& property_name) { |
| CSSPropertyID property_id = cssPropertyID(property_name); |
| if (!property_id) |
| return String(); |
| if (property_id == CSSPropertyVariable) |
| return PropertySet().GetPropertyValue(AtomicString(property_name)); |
| return PropertySet().GetPropertyValue(property_id); |
| } |
| |
| String AbstractPropertySetCSSStyleDeclaration::getPropertyPriority( |
| const String& property_name) { |
| CSSPropertyID property_id = cssPropertyID(property_name); |
| if (!property_id) |
| return String(); |
| |
| bool important = false; |
| if (property_id == CSSPropertyVariable) |
| important = PropertySet().PropertyIsImportant(AtomicString(property_name)); |
| else |
| important = PropertySet().PropertyIsImportant(property_id); |
| return important ? "important" : ""; |
| } |
| |
| String AbstractPropertySetCSSStyleDeclaration::GetPropertyShorthand( |
| const String& property_name) { |
| CSSPropertyID property_id = cssPropertyID(property_name); |
| |
| // Custom properties don't have shorthands, so we can ignore them here. |
| if (!property_id || !CSSProperty::Get(property_id).IsLonghand()) |
| return String(); |
| CSSPropertyID shorthand_id = PropertySet().GetPropertyShorthand(property_id); |
| if (!shorthand_id) |
| return String(); |
| return getPropertyNameString(shorthand_id); |
| } |
| |
| bool AbstractPropertySetCSSStyleDeclaration::IsPropertyImplicit( |
| const String& property_name) { |
| CSSPropertyID property_id = cssPropertyID(property_name); |
| |
| // Custom properties don't have shorthands, so we can ignore them here. |
| if (!property_id || property_id == CSSPropertyVariable) |
| return false; |
| return PropertySet().IsPropertyImplicit(property_id); |
| } |
| |
| void AbstractPropertySetCSSStyleDeclaration::setProperty( |
| const ExecutionContext* execution_context, |
| const String& property_name, |
| const String& value, |
| const String& priority, |
| ExceptionState& exception_state) { |
| CSSPropertyID property_id = unresolvedCSSPropertyID(property_name); |
| if (!property_id) |
| return; |
| |
| bool important = DeprecatedEqualIgnoringCase(priority, "important"); |
| if (!important && !priority.IsEmpty()) |
| return; |
| |
| SetPropertyInternal(property_id, property_name, value, important, |
| execution_context->SecureContextMode(), exception_state); |
| } |
| |
| String AbstractPropertySetCSSStyleDeclaration::removeProperty( |
| const String& property_name, |
| ExceptionState& exception_state) { |
| CSSPropertyID property_id = cssPropertyID(property_name); |
| if (!property_id) |
| return String(); |
| |
| StyleAttributeMutationScope mutation_scope(this); |
| WillMutate(); |
| |
| String result; |
| bool changed = false; |
| if (property_id == CSSPropertyVariable) |
| changed = |
| PropertySet().RemoveProperty(AtomicString(property_name), &result); |
| else |
| changed = PropertySet().RemoveProperty(property_id, &result); |
| |
| DidMutate(changed ? kPropertyChanged : kNoChanges); |
| |
| if (changed) |
| mutation_scope.EnqueueMutationRecord(); |
| return result; |
| } |
| |
| const CSSValue* |
| AbstractPropertySetCSSStyleDeclaration::GetPropertyCSSValueInternal( |
| CSSPropertyID property_id) { |
| return PropertySet().GetPropertyCSSValue(property_id); |
| } |
| |
| const CSSValue* |
| AbstractPropertySetCSSStyleDeclaration::GetPropertyCSSValueInternal( |
| AtomicString custom_property_name) { |
| return PropertySet().GetPropertyCSSValue(custom_property_name); |
| } |
| |
| String AbstractPropertySetCSSStyleDeclaration::GetPropertyValueInternal( |
| CSSPropertyID property_id) { |
| return PropertySet().GetPropertyValue(property_id); |
| } |
| |
| DISABLE_CFI_PERF |
| void AbstractPropertySetCSSStyleDeclaration::SetPropertyInternal( |
| CSSPropertyID unresolved_property, |
| const String& custom_property_name, |
| const String& value, |
| bool important, |
| SecureContextMode secure_context_mode, |
| ExceptionState&) { |
| StyleAttributeMutationScope mutation_scope(this); |
| WillMutate(); |
| |
| bool did_change = false; |
| if (unresolved_property == CSSPropertyVariable) { |
| AtomicString atomic_name(custom_property_name); |
| |
| bool is_animation_tainted = IsKeyframeStyle(); |
| did_change = PropertySet() |
| .SetProperty(atomic_name, GetPropertyRegistry(), value, |
| important, secure_context_mode, |
| ContextStyleSheet(), is_animation_tainted) |
| .did_change; |
| } else { |
| did_change = PropertySet() |
| .SetProperty(unresolved_property, value, important, |
| secure_context_mode, ContextStyleSheet()) |
| .did_change; |
| } |
| |
| DidMutate(did_change ? kPropertyChanged : kNoChanges); |
| |
| if (!did_change) |
| return; |
| |
| Element* parent = ParentElement(); |
| if (parent) |
| parent->GetDocument().GetStyleEngine().AttributeChangedForElement( |
| HTMLNames::styleAttr, *parent); |
| mutation_scope.EnqueueMutationRecord(); |
| } |
| |
| DISABLE_CFI_PERF |
| StyleSheetContents* AbstractPropertySetCSSStyleDeclaration::ContextStyleSheet() |
| const { |
| CSSStyleSheet* css_style_sheet = ParentStyleSheet(); |
| return css_style_sheet ? css_style_sheet->Contents() : nullptr; |
| } |
| |
| bool AbstractPropertySetCSSStyleDeclaration::CssPropertyMatches( |
| CSSPropertyID property_id, |
| const CSSValue* property_value) const { |
| return PropertySet().PropertyMatches(property_id, *property_value); |
| } |
| |
| void AbstractPropertySetCSSStyleDeclaration::Trace(blink::Visitor* visitor) { |
| CSSStyleDeclaration::Trace(visitor); |
| } |
| |
| StyleRuleCSSStyleDeclaration::StyleRuleCSSStyleDeclaration( |
| MutableCSSPropertyValueSet& property_set_arg, |
| CSSRule* parent_rule) |
| : PropertySetCSSStyleDeclaration(property_set_arg), |
| parent_rule_(parent_rule) {} |
| |
| StyleRuleCSSStyleDeclaration::~StyleRuleCSSStyleDeclaration() {} |
| |
| void StyleRuleCSSStyleDeclaration::WillMutate() { |
| if (parent_rule_ && parent_rule_->parentStyleSheet()) |
| parent_rule_->parentStyleSheet()->WillMutateRules(); |
| } |
| |
| void StyleRuleCSSStyleDeclaration::DidMutate(MutationType type) { |
| // Style sheet mutation needs to be signaled even if the change failed. |
| // willMutateRules/didMutateRules must pair. |
| if (parent_rule_ && parent_rule_->parentStyleSheet()) |
| parent_rule_->parentStyleSheet()->DidMutateRules(); |
| } |
| |
| CSSStyleSheet* StyleRuleCSSStyleDeclaration::ParentStyleSheet() const { |
| return parent_rule_ ? parent_rule_->parentStyleSheet() : nullptr; |
| } |
| |
| void StyleRuleCSSStyleDeclaration::Reattach( |
| MutableCSSPropertyValueSet& property_set) { |
| property_set_ = &property_set; |
| } |
| |
| PropertyRegistry* StyleRuleCSSStyleDeclaration::GetPropertyRegistry() const { |
| CSSStyleSheet* sheet = parent_rule_->parentStyleSheet(); |
| if (!sheet) |
| return nullptr; |
| Node* node = sheet->ownerNode(); |
| if (!node) |
| return nullptr; |
| return node->GetDocument().GetPropertyRegistry(); |
| } |
| |
| void StyleRuleCSSStyleDeclaration::Trace(blink::Visitor* visitor) { |
| visitor->Trace(parent_rule_); |
| PropertySetCSSStyleDeclaration::Trace(visitor); |
| } |
| |
| void StyleRuleCSSStyleDeclaration::TraceWrappers( |
| const ScriptWrappableVisitor* visitor) const { |
| visitor->TraceWrappers(parent_rule_); |
| PropertySetCSSStyleDeclaration::TraceWrappers(visitor); |
| } |
| |
| MutableCSSPropertyValueSet& InlineCSSStyleDeclaration::PropertySet() const { |
| return parent_element_->EnsureMutableInlineStyle(); |
| } |
| |
| void InlineCSSStyleDeclaration::DidMutate(MutationType type) { |
| if (type == kNoChanges) |
| return; |
| |
| if (!parent_element_) |
| return; |
| |
| parent_element_->ClearMutableInlineStyleIfEmpty(); |
| parent_element_->SetNeedsStyleRecalc( |
| kLocalStyleChange, StyleChangeReasonForTracing::Create( |
| StyleChangeReason::kInlineCSSStyleMutated)); |
| parent_element_->InvalidateStyleAttribute(); |
| StyleAttributeMutationScope(this).DidInvalidateStyleAttr(); |
| } |
| |
| CSSStyleSheet* InlineCSSStyleDeclaration::ParentStyleSheet() const { |
| return parent_element_ ? &parent_element_->GetDocument().ElementSheet() |
| : nullptr; |
| } |
| |
| PropertyRegistry* InlineCSSStyleDeclaration::GetPropertyRegistry() const { |
| return parent_element_ ? parent_element_->GetDocument().GetPropertyRegistry() |
| : nullptr; |
| } |
| |
| void InlineCSSStyleDeclaration::Trace(blink::Visitor* visitor) { |
| visitor->Trace(parent_element_); |
| AbstractPropertySetCSSStyleDeclaration::Trace(visitor); |
| } |
| |
| } // namespace blink |