| /* |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) |
| * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com) |
| * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. |
| * All rights reserved. |
| * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> |
| * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org> |
| * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. |
| * (http://www.torchmobile.com/) |
| * Copyright (c) 2011, Code Aurora Forum. All rights reserved. |
| * Copyright (C) Research In Motion Limited 2011. All rights reserved. |
| * Copyright (C) 2013 Google Inc. 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 "third_party/blink/renderer/core/css/resolver/matched_properties_cache.h" |
| |
| #include "third_party/blink/renderer/core/css/css_property_value_set.h" |
| #include "third_party/blink/renderer/core/css/properties/css_property_ref.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h" |
| #include "third_party/blink/renderer/core/style/computed_style.h" |
| #include "third_party/blink/renderer/platform/runtime_enabled_features.h" |
| #include "third_party/blink/renderer/platform/wtf/text/string_hasher.h" |
| |
| namespace blink { |
| |
| static unsigned ComputeMatchedPropertiesHash(const MatchResult& result) { |
| const MatchedPropertiesVector& vector = result.GetMatchedProperties(); |
| return StringHasher::HashMemory(vector.data(), |
| sizeof(MatchedProperties) * vector.size()); |
| } |
| |
| void CachedMatchedProperties::Set(const ComputedStyle& style, |
| const ComputedStyle& parent_style, |
| const MatchedPropertiesVector& properties) { |
| for (const auto& new_matched_properties : properties) { |
| matched_properties.push_back(new_matched_properties.properties); |
| matched_properties_types.push_back(new_matched_properties.types_); |
| } |
| |
| // Note that we don't cache the original ComputedStyle instance. It may be |
| // further modified. The ComputedStyle in the cache is really just a holder |
| // for the substructures and never used as-is. |
| this->computed_style = ComputedStyle::Clone(style); |
| this->parent_computed_style = ComputedStyle::Clone(parent_style); |
| } |
| |
| void CachedMatchedProperties::Clear() { |
| matched_properties.clear(); |
| matched_properties_types.clear(); |
| computed_style = nullptr; |
| parent_computed_style = nullptr; |
| } |
| |
| bool CachedMatchedProperties::DependenciesEqual( |
| const StyleResolverState& state) { |
| if (!state.ParentStyle()) |
| return false; |
| if ((parent_computed_style->IsEnsuredInDisplayNone() || |
| computed_style->IsEnsuredOutsideFlatTree()) && |
| !state.ParentStyle()->IsEnsuredInDisplayNone() && |
| !state.Style()->IsEnsuredOutsideFlatTree()) { |
| // If we cached a ComputedStyle in a display:none subtree, or outside the |
| // flat tree, we would not have triggered fetches for external resources |
| // and have StylePendingImages in the ComputedStyle. Instead of having to |
| // inspect the cached ComputedStyle for such resources, don't use a cached |
| // ComputedStyle when it was cached in display:none but is now rendered. |
| return false; |
| } |
| |
| if (parent_computed_style->GetWritingMode() != |
| state.ParentStyle()->GetWritingMode()) { |
| return false; |
| } |
| if (parent_computed_style->Direction() != state.ParentStyle()->Direction()) |
| return false; |
| if (computed_style->HasVariableReferenceFromNonInheritedProperty()) { |
| if (parent_computed_style->InheritedVariables() != |
| state.ParentStyle()->InheritedVariables()) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| MatchedPropertiesCache::MatchedPropertiesCache() = default; |
| |
| MatchedPropertiesCache::Key::Key(const MatchResult& result) |
| : Key(result, |
| result.IsCacheable() ? ComputeMatchedPropertiesHash(result) |
| : HashTraits<unsigned>::EmptyValue()) {} |
| |
| MatchedPropertiesCache::Key::Key(const MatchResult& result, unsigned hash) |
| : result_(result), hash_(hash) {} |
| |
| const CachedMatchedProperties* MatchedPropertiesCache::Find( |
| const Key& key, |
| const StyleResolverState& style_resolver_state) { |
| DCHECK(key.IsValid()); |
| Cache::iterator it = cache_.find(key.hash_); |
| if (it == cache_.end()) |
| return nullptr; |
| CachedMatchedProperties* cache_item = it->value.Get(); |
| if (!cache_item) |
| return nullptr; |
| if (*cache_item != key.result_.GetMatchedProperties()) |
| return nullptr; |
| if (cache_item->computed_style->InsideLink() != |
| style_resolver_state.Style()->InsideLink()) |
| return nullptr; |
| if (!cache_item->DependenciesEqual(style_resolver_state)) |
| return nullptr; |
| return cache_item; |
| } |
| |
| bool CachedMatchedProperties::operator==( |
| const MatchedPropertiesVector& properties) { |
| if (properties.size() != matched_properties.size()) |
| return false; |
| for (wtf_size_t i = 0; i < properties.size(); ++i) { |
| if (properties[i].properties != matched_properties[i]) |
| return false; |
| if (properties[i].types_.link_match_type != |
| matched_properties_types[i].link_match_type) |
| return false; |
| if (properties[i].types_.tree_order != |
| matched_properties_types[i].tree_order) |
| return false; |
| if (properties[i].types_.valid_property_filter != |
| matched_properties_types[i].valid_property_filter) |
| return false; |
| } |
| return true; |
| } |
| |
| bool CachedMatchedProperties::operator!=( |
| const MatchedPropertiesVector& properties) { |
| return !(*this == properties); |
| } |
| |
| void MatchedPropertiesCache::Add(const Key& key, |
| const ComputedStyle& style, |
| const ComputedStyle& parent_style) { |
| DCHECK(key.IsValid()); |
| Cache::AddResult add_result = cache_.insert(key.hash_, nullptr); |
| if (add_result.is_new_entry || !add_result.stored_value->value) { |
| add_result.stored_value->value = |
| MakeGarbageCollected<CachedMatchedProperties>(); |
| } |
| |
| CachedMatchedProperties* cache_item = add_result.stored_value->value.Get(); |
| if (!add_result.is_new_entry) |
| cache_item->Clear(); |
| |
| cache_item->Set(style, parent_style, key.result_.GetMatchedProperties()); |
| } |
| |
| void MatchedPropertiesCache::Clear() { |
| // MatchedPropertiesCache must be cleared promptly because some |
| // destructors in the properties (e.g., ~FontFallbackList) expect that |
| // the destructors are called promptly without relying on a GC timing. |
| for (auto& cache_entry : cache_) { |
| if (cache_entry.value) |
| cache_entry.value->Clear(); |
| } |
| cache_.clear(); |
| } |
| |
| void MatchedPropertiesCache::ClearViewportDependent() { |
| Vector<unsigned, 16> to_remove; |
| for (const auto& cache_entry : cache_) { |
| CachedMatchedProperties* cache_item = cache_entry.value.Get(); |
| if (cache_item && cache_item->computed_style->HasViewportUnits()) |
| to_remove.push_back(cache_entry.key); |
| } |
| cache_.RemoveAll(to_remove); |
| } |
| |
| bool MatchedPropertiesCache::IsStyleCacheable(const ComputedStyle& style) { |
| // Content property with attr() values depend on the attribute value of the |
| // originating element, thus we cannot cache based on the matched properties |
| // because the value of content is retrieved from the attribute at apply time. |
| if (style.HasAttrContent()) |
| return false; |
| if (style.Zoom() != ComputedStyleInitialValues::InitialZoom()) |
| return false; |
| if (style.TextAutosizingMultiplier() != 1) |
| return false; |
| // -internal-light-dark() values in UA sheets have different computed values |
| // based on the used value of color-scheme. |
| if (style.HasNonInheritedLightDarkValue()) |
| return false; |
| return true; |
| } |
| |
| bool MatchedPropertiesCache::IsCacheable(const StyleResolverState& state) { |
| const ComputedStyle& style = *state.Style(); |
| const ComputedStyle& parent_style = *state.ParentStyle(); |
| |
| if (!IsStyleCacheable(style)) |
| return false; |
| |
| // The cache assumes static knowledge about which properties are inherited. |
| // Without a flat tree parent, StyleBuilder::ApplyProperty will not |
| // SetChildHasExplicitInheritance on the parent style. |
| if (!state.ParentNode() || parent_style.ChildHasExplicitInheritance()) |
| return false; |
| |
| return true; |
| } |
| |
| void MatchedPropertiesCache::Trace(Visitor* visitor) const { |
| visitor->Trace(cache_); |
| visitor->RegisterWeakCallbackMethod< |
| MatchedPropertiesCache, |
| &MatchedPropertiesCache::RemoveCachedMatchedPropertiesWithDeadEntries>( |
| this); |
| } |
| |
| void MatchedPropertiesCache::RemoveCachedMatchedPropertiesWithDeadEntries( |
| const LivenessBroker& info) { |
| Vector<unsigned> to_remove; |
| for (const auto& entry_pair : cache_) { |
| // A nullptr value indicates that the entry is currently being created; see |
| // |MatchedPropertiesCache::Add|. Keep such entries. |
| if (!entry_pair.value) |
| continue; |
| for (const auto& matched_properties : |
| entry_pair.value->matched_properties) { |
| if (!info.IsHeapObjectAlive(matched_properties)) { |
| to_remove.push_back(entry_pair.key); |
| break; |
| } |
| } |
| } |
| // Allocation is forbidden during executing weak callbacks, so the data |
| // structure will not be rehashed here. The next insertion/deletion from |
| // regular code will take care of shrinking accordingly. |
| cache_.RemoveAll(to_remove); |
| } |
| |
| } // namespace blink |