| /* |
| * Copyright (C) 2013 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "core/dom/CSSSelectorWatch.h" |
| |
| #include "core/css/StylePropertySet.h" |
| #include "core/css/parser/CSSParser.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/ExecutionContext.h" |
| #include "core/dom/StyleEngine.h" |
| #include "core/dom/TaskRunnerHelper.h" |
| #include "core/frame/LocalFrame.h" |
| #include "core/frame/LocalFrameClient.h" |
| |
| namespace blink { |
| |
| // The address of this string is important; its value is just documentation. |
| static const char kSupplementNameWatch[] = "CSSSelectorWatch"; |
| |
| CSSSelectorWatch::CSSSelectorWatch(Document& document) |
| : Supplement<Document>(document), |
| callback_selector_change_timer_( |
| TaskRunnerHelper::Get(TaskType::kUnspecedTimer, &document), |
| this, |
| &CSSSelectorWatch::CallbackSelectorChangeTimerFired), |
| timer_expirations_(0) {} |
| |
| CSSSelectorWatch& CSSSelectorWatch::From(Document& document) { |
| CSSSelectorWatch* watch = FromIfExists(document); |
| if (!watch) { |
| watch = new CSSSelectorWatch(document); |
| Supplement<Document>::ProvideTo(document, kSupplementNameWatch, watch); |
| } |
| return *watch; |
| } |
| |
| CSSSelectorWatch* CSSSelectorWatch::FromIfExists(Document& document) { |
| return static_cast<CSSSelectorWatch*>( |
| Supplement<Document>::From(document, kSupplementNameWatch)); |
| } |
| |
| void CSSSelectorWatch::CallbackSelectorChangeTimerFired(TimerBase*) { |
| // Should be ensured by updateSelectorMatches(): |
| DCHECK(!added_selectors_.IsEmpty() || !removed_selectors_.IsEmpty()); |
| |
| if (timer_expirations_ < 1) { |
| timer_expirations_++; |
| callback_selector_change_timer_.StartOneShot(0, BLINK_FROM_HERE); |
| return; |
| } |
| if (GetSupplementable()->GetFrame()) { |
| Vector<String> added_selectors; |
| Vector<String> removed_selectors; |
| CopyToVector(added_selectors_, added_selectors); |
| CopyToVector(removed_selectors_, removed_selectors); |
| GetSupplementable()->GetFrame()->Loader().Client()->SelectorMatchChanged( |
| added_selectors, removed_selectors); |
| } |
| added_selectors_.clear(); |
| removed_selectors_.clear(); |
| timer_expirations_ = 0; |
| } |
| |
| void CSSSelectorWatch::UpdateSelectorMatches( |
| const Vector<String>& removed_selectors, |
| const Vector<String>& added_selectors) { |
| bool should_update_timer = false; |
| |
| for (const auto& selector : removed_selectors) { |
| if (!matching_callback_selectors_.erase(selector)) |
| continue; |
| |
| // Count reached 0. |
| should_update_timer = true; |
| auto it = added_selectors_.find(selector); |
| if (it != added_selectors_.end()) |
| added_selectors_.erase(it); |
| else |
| removed_selectors_.insert(selector); |
| } |
| |
| for (const auto& selector : added_selectors) { |
| HashCountedSet<String>::AddResult result = |
| matching_callback_selectors_.insert(selector); |
| if (!result.is_new_entry) |
| continue; |
| |
| should_update_timer = true; |
| auto it = removed_selectors_.find(selector); |
| if (it != removed_selectors_.end()) |
| removed_selectors_.erase(it); |
| else |
| added_selectors_.insert(selector); |
| } |
| |
| if (!should_update_timer) |
| return; |
| |
| if (removed_selectors_.IsEmpty() && added_selectors_.IsEmpty()) { |
| if (callback_selector_change_timer_.IsActive()) { |
| timer_expirations_ = 0; |
| callback_selector_change_timer_.Stop(); |
| } |
| } else { |
| timer_expirations_ = 0; |
| if (!callback_selector_change_timer_.IsActive()) |
| callback_selector_change_timer_.StartOneShot(0, BLINK_FROM_HERE); |
| } |
| } |
| |
| static bool AllCompound(const CSSSelectorList& selector_list) { |
| for (const CSSSelector* selector = selector_list.First(); selector; |
| selector = selector_list.Next(*selector)) { |
| if (!selector->IsCompound()) |
| return false; |
| } |
| return true; |
| } |
| |
| void CSSSelectorWatch::WatchCSSSelectors(const Vector<String>& selectors) { |
| watched_callback_selectors_.clear(); |
| |
| StylePropertySet* callback_property_set = |
| ImmutableStylePropertySet::Create(nullptr, 0, kUASheetMode); |
| |
| CSSParserContext* context = CSSParserContext::Create(kUASheetMode); |
| for (const auto& selector : selectors) { |
| CSSSelectorList selector_list = |
| CSSParser::ParseSelector(context, nullptr, selector); |
| if (!selector_list.IsValid()) |
| continue; |
| |
| // Only accept Compound Selectors, since they're cheaper to match. |
| if (!AllCompound(selector_list)) |
| continue; |
| |
| watched_callback_selectors_.push_back( |
| StyleRule::Create(std::move(selector_list), callback_property_set)); |
| } |
| GetSupplementable()->GetStyleEngine().WatchedSelectorsChanged(); |
| } |
| |
| DEFINE_TRACE(CSSSelectorWatch) { |
| visitor->Trace(watched_callback_selectors_); |
| Supplement<Document>::Trace(visitor); |
| } |
| |
| } // namespace blink |