2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "core/dom/CSSSelectorWatch.h"
34 #include "core/css/StylePropertySet.h"
35 #include "core/css/parser/CSSParser.h"
36 #include "core/dom/Document.h"
37 #include "core/dom/ExecutionContext.h"
38 #include "core/frame/LocalFrame.h"
39 #include "core/loader/FrameLoaderClient.h"
40 #include "core/style/StyleRareNonInheritedData.h"
44 // The address of this string is important; its value is just documentation.
45 static const char kSupplementName
[] = "CSSSelectorWatch";
47 CSSSelectorWatch::CSSSelectorWatch(Document
& document
)
48 : m_document(document
)
49 , m_callbackSelectorChangeTimer(this, &CSSSelectorWatch::callbackSelectorChangeTimerFired
)
50 , m_timerExpirations(0)
54 CSSSelectorWatch
& CSSSelectorWatch::from(Document
& document
)
56 CSSSelectorWatch
* watch
= fromIfExists(document
);
58 watch
= new CSSSelectorWatch(document
);
59 WillBeHeapSupplement
<Document
>::provideTo(document
, kSupplementName
, adoptPtrWillBeNoop(watch
));
64 CSSSelectorWatch
* CSSSelectorWatch::fromIfExists(Document
& document
)
66 return static_cast<CSSSelectorWatch
*>(WillBeHeapSupplement
<Document
>::from(document
, kSupplementName
));
69 void CSSSelectorWatch::callbackSelectorChangeTimerFired(Timer
<CSSSelectorWatch
>*)
71 // Should be ensured by updateSelectorMatches():
72 ASSERT(!m_addedSelectors
.isEmpty() || !m_removedSelectors
.isEmpty());
74 if (m_timerExpirations
< 1) {
76 m_callbackSelectorChangeTimer
.startOneShot(0, FROM_HERE
);
79 if (document().frame()) {
80 Vector
<String
> addedSelectors
;
81 Vector
<String
> removedSelectors
;
82 copyToVector(m_addedSelectors
, addedSelectors
);
83 copyToVector(m_removedSelectors
, removedSelectors
);
84 document().frame()->loader().client()->selectorMatchChanged(addedSelectors
, removedSelectors
);
86 m_addedSelectors
.clear();
87 m_removedSelectors
.clear();
88 m_timerExpirations
= 0;
91 void CSSSelectorWatch::updateSelectorMatches(const Vector
<String
>& removedSelectors
, const Vector
<String
>& addedSelectors
)
93 bool shouldUpdateTimer
= false;
95 for (unsigned i
= 0; i
< removedSelectors
.size(); ++i
) {
96 const String
& selector
= removedSelectors
[i
];
97 if (!m_matchingCallbackSelectors
.remove(selector
))
101 shouldUpdateTimer
= true;
102 if (m_addedSelectors
.contains(selector
))
103 m_addedSelectors
.remove(selector
);
105 m_removedSelectors
.add(selector
);
108 for (unsigned i
= 0; i
< addedSelectors
.size(); ++i
) {
109 const String
& selector
= addedSelectors
[i
];
110 HashCountedSet
<String
>::AddResult result
= m_matchingCallbackSelectors
.add(selector
);
111 if (!result
.isNewEntry
)
114 shouldUpdateTimer
= true;
115 if (m_removedSelectors
.contains(selector
))
116 m_removedSelectors
.remove(selector
);
118 m_addedSelectors
.add(selector
);
121 if (!shouldUpdateTimer
)
124 if (m_removedSelectors
.isEmpty() && m_addedSelectors
.isEmpty()) {
125 if (m_callbackSelectorChangeTimer
.isActive()) {
126 m_timerExpirations
= 0;
127 m_callbackSelectorChangeTimer
.stop();
130 m_timerExpirations
= 0;
131 if (!m_callbackSelectorChangeTimer
.isActive())
132 m_callbackSelectorChangeTimer
.startOneShot(0, FROM_HERE
);
136 static bool allCompound(const CSSSelectorList
& selectorList
)
138 for (const CSSSelector
* selector
= selectorList
.first(); selector
; selector
= selectorList
.next(*selector
)) {
139 if (!selector
->isCompound())
145 void CSSSelectorWatch::watchCSSSelectors(const Vector
<String
>& selectors
)
147 m_watchedCallbackSelectors
.clear();
149 const RefPtrWillBeRawPtr
<StylePropertySet
> callbackPropertySet
= ImmutableStylePropertySet::create(nullptr, 0, UASheetMode
);
151 CSSSelectorList selectorList
;
152 for (unsigned i
= 0; i
< selectors
.size(); ++i
) {
153 CSSParser::parseSelector(CSSParserContext(UASheetMode
, 0), selectors
[i
], selectorList
);
154 if (!selectorList
.isValid())
157 // Only accept Compound Selectors, since they're cheaper to match.
158 if (!allCompound(selectorList
))
161 m_watchedCallbackSelectors
.append(StyleRule::create(selectorList
, callbackPropertySet
));
163 document().changedSelectorWatch();
166 DEFINE_TRACE(CSSSelectorWatch
)
168 visitor
->trace(m_watchedCallbackSelectors
);
169 visitor
->trace(m_document
);
170 WillBeHeapSupplement
<Document
>::trace(visitor
);