1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker.h"
6
7 #include "base/bind.h"
8 #include "base/memory/ptr_util.h"
9 #include "base/stl_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/values.h"
12 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
13 #include "content/public/browser/web_contents.h"
14 #include "extensions/common/api/declarative/declarative_constants.h"
15 #include "extensions/common/permissions/permissions_data.h"
16
17 namespace extensions {
18
19 namespace {
20
21 const char kIsBookmarkedInvalidTypeOfParameter[] =
22 "Attribute '%s' has an invalid type";
23 const char kIsBookmarkedRequiresBookmarkPermission[] =
24 "Property 'isBookmarked' requires 'bookmarks' permission";
25
HasBookmarkAPIPermission(const Extension * extension)26 bool HasBookmarkAPIPermission(const Extension* extension) {
27 return extension->permissions_data()->HasAPIPermission(
28 APIPermission::kBookmark);
29 }
30
31 } // namespace
32
33 //
34 // DeclarativeContentIsBookmarkedPredicate
35 //
36
37 DeclarativeContentIsBookmarkedPredicate::
~DeclarativeContentIsBookmarkedPredicate()38 ~DeclarativeContentIsBookmarkedPredicate() {
39 }
40
IsIgnored() const41 bool DeclarativeContentIsBookmarkedPredicate::IsIgnored() const {
42 return !HasBookmarkAPIPermission(extension_.get());
43 }
44
45 // static
46 std::unique_ptr<DeclarativeContentIsBookmarkedPredicate>
Create(ContentPredicateEvaluator * evaluator,const Extension * extension,const base::Value & value,std::string * error)47 DeclarativeContentIsBookmarkedPredicate::Create(
48 ContentPredicateEvaluator* evaluator,
49 const Extension* extension,
50 const base::Value& value,
51 std::string* error) {
52 bool is_bookmarked = false;
53 if (value.GetAsBoolean(&is_bookmarked)) {
54 if (!HasBookmarkAPIPermission(extension)) {
55 *error = kIsBookmarkedRequiresBookmarkPermission;
56 return std::unique_ptr<DeclarativeContentIsBookmarkedPredicate>();
57 } else {
58 return base::WrapUnique(new DeclarativeContentIsBookmarkedPredicate(
59 evaluator, extension, is_bookmarked));
60 }
61 } else {
62 *error = base::StringPrintf(kIsBookmarkedInvalidTypeOfParameter,
63 declarative_content_constants::kIsBookmarked);
64 return std::unique_ptr<DeclarativeContentIsBookmarkedPredicate>();
65 }
66 }
67
68 ContentPredicateEvaluator*
GetEvaluator() const69 DeclarativeContentIsBookmarkedPredicate::GetEvaluator() const {
70 return evaluator_;
71 }
72
73 DeclarativeContentIsBookmarkedPredicate::
DeclarativeContentIsBookmarkedPredicate(ContentPredicateEvaluator * evaluator,scoped_refptr<const Extension> extension,bool is_bookmarked)74 DeclarativeContentIsBookmarkedPredicate(
75 ContentPredicateEvaluator* evaluator,
76 scoped_refptr<const Extension> extension,
77 bool is_bookmarked)
78 : evaluator_(evaluator),
79 extension_(extension),
80 is_bookmarked_(is_bookmarked) {
81 }
82
83 //
84 // PerWebContentsTracker
85 //
86
87 DeclarativeContentIsBookmarkedConditionTracker::PerWebContentsTracker::
PerWebContentsTracker(content::WebContents * contents,const RequestEvaluationCallback & request_evaluation,const WebContentsDestroyedCallback & web_contents_destroyed)88 PerWebContentsTracker(
89 content::WebContents* contents,
90 const RequestEvaluationCallback& request_evaluation,
91 const WebContentsDestroyedCallback& web_contents_destroyed)
92 : WebContentsObserver(contents),
93 request_evaluation_(request_evaluation),
94 web_contents_destroyed_(web_contents_destroyed) {
95 is_url_bookmarked_ = IsCurrentUrlBookmarked();
96 request_evaluation_.Run(web_contents());
97 }
98
99 DeclarativeContentIsBookmarkedConditionTracker::PerWebContentsTracker::
~PerWebContentsTracker()100 ~PerWebContentsTracker() {
101 }
102
103 void DeclarativeContentIsBookmarkedConditionTracker::PerWebContentsTracker::
BookmarkAddedForUrl(const GURL & url)104 BookmarkAddedForUrl(const GURL& url) {
105 if (web_contents()->GetVisibleURL() == url) {
106 is_url_bookmarked_ = true;
107 request_evaluation_.Run(web_contents());
108 }
109 }
110
111 void DeclarativeContentIsBookmarkedConditionTracker::PerWebContentsTracker::
BookmarkRemovedForUrls(const std::set<GURL> & urls)112 BookmarkRemovedForUrls(const std::set<GURL>& urls) {
113 if (base::Contains(urls, web_contents()->GetVisibleURL())) {
114 is_url_bookmarked_ = false;
115 request_evaluation_.Run(web_contents());
116 }
117 }
118
119 void DeclarativeContentIsBookmarkedConditionTracker::PerWebContentsTracker::
UpdateState(bool request_evaluation_if_unchanged)120 UpdateState(bool request_evaluation_if_unchanged) {
121 bool state_changed =
122 IsCurrentUrlBookmarked() != is_url_bookmarked_;
123 if (state_changed)
124 is_url_bookmarked_ = !is_url_bookmarked_;
125 if (state_changed || request_evaluation_if_unchanged)
126 request_evaluation_.Run(web_contents());
127 }
128
129 bool DeclarativeContentIsBookmarkedConditionTracker::PerWebContentsTracker::
IsCurrentUrlBookmarked()130 IsCurrentUrlBookmarked() {
131 bookmarks::BookmarkModel* bookmark_model =
132 BookmarkModelFactory::GetForBrowserContext(
133 web_contents()->GetBrowserContext());
134 // BookmarkModel can be null during unit test execution.
135 return bookmark_model &&
136 bookmark_model->IsBookmarked(web_contents()->GetVisibleURL());
137 }
138
139 void DeclarativeContentIsBookmarkedConditionTracker::PerWebContentsTracker::
WebContentsDestroyed()140 WebContentsDestroyed() {
141 web_contents_destroyed_.Run(web_contents());
142 }
143
144 //
145 // DeclarativeContentIsBookmarkedConditionTracker
146 //
147
148 DeclarativeContentIsBookmarkedConditionTracker::
DeclarativeContentIsBookmarkedConditionTracker(content::BrowserContext * context,Delegate * delegate)149 DeclarativeContentIsBookmarkedConditionTracker(
150 content::BrowserContext* context,
151 Delegate* delegate)
152 : delegate_(delegate), extensive_bookmark_changes_in_progress_(0) {
153 bookmarks::BookmarkModel* bookmark_model =
154 BookmarkModelFactory::GetForBrowserContext(context);
155 // Can be null during unit test execution.
156 if (bookmark_model)
157 scoped_bookmarks_observer_.Add(bookmark_model);
158 }
159
160 DeclarativeContentIsBookmarkedConditionTracker::
~DeclarativeContentIsBookmarkedConditionTracker()161 ~DeclarativeContentIsBookmarkedConditionTracker() {
162 }
163
164 std::string DeclarativeContentIsBookmarkedConditionTracker::
GetPredicateApiAttributeName() const165 GetPredicateApiAttributeName() const {
166 return declarative_content_constants::kIsBookmarked;
167 }
168
169 std::unique_ptr<const ContentPredicate>
CreatePredicate(const Extension * extension,const base::Value & value,std::string * error)170 DeclarativeContentIsBookmarkedConditionTracker::CreatePredicate(
171 const Extension* extension,
172 const base::Value& value,
173 std::string* error) {
174 return DeclarativeContentIsBookmarkedPredicate::Create(this, extension, value,
175 error);
176 }
177
178 // We don't have any centralized state to update for new predicates, so we don't
179 // need to take any action here or in StopTrackingPredicates().
TrackPredicates(const std::map<const void *,std::vector<const ContentPredicate * >> & predicates)180 void DeclarativeContentIsBookmarkedConditionTracker::TrackPredicates(
181 const std::map<const void*, std::vector<const ContentPredicate*>>&
182 predicates) {
183 }
184
StopTrackingPredicates(const std::vector<const void * > & predicate_groups)185 void DeclarativeContentIsBookmarkedConditionTracker::StopTrackingPredicates(
186 const std::vector<const void*>& predicate_groups) {
187 }
188
TrackForWebContents(content::WebContents * contents)189 void DeclarativeContentIsBookmarkedConditionTracker::TrackForWebContents(
190 content::WebContents* contents) {
191 per_web_contents_tracker_[contents] = std::make_unique<PerWebContentsTracker>(
192 contents,
193 base::Bind(&Delegate::RequestEvaluation, base::Unretained(delegate_)),
194 base::Bind(&DeclarativeContentIsBookmarkedConditionTracker::
195 DeletePerWebContentsTracker,
196 base::Unretained(this)));
197 }
198
OnWebContentsNavigation(content::WebContents * contents,content::NavigationHandle * navigation_handle)199 void DeclarativeContentIsBookmarkedConditionTracker::OnWebContentsNavigation(
200 content::WebContents* contents,
201 content::NavigationHandle* navigation_handle) {
202 DCHECK(base::Contains(per_web_contents_tracker_, contents));
203 per_web_contents_tracker_[contents]->UpdateState(true);
204 }
205
EvaluatePredicate(const ContentPredicate * predicate,content::WebContents * tab) const206 bool DeclarativeContentIsBookmarkedConditionTracker::EvaluatePredicate(
207 const ContentPredicate* predicate,
208 content::WebContents* tab) const {
209 DCHECK_EQ(this, predicate->GetEvaluator());
210 const DeclarativeContentIsBookmarkedPredicate* typed_predicate =
211 static_cast<const DeclarativeContentIsBookmarkedPredicate*>(predicate);
212 auto loc = per_web_contents_tracker_.find(tab);
213 DCHECK(loc != per_web_contents_tracker_.end());
214 return loc->second->is_url_bookmarked() == typed_predicate->is_bookmarked();
215 }
216
BookmarkModelChanged()217 void DeclarativeContentIsBookmarkedConditionTracker::BookmarkModelChanged() {}
218
BookmarkNodeAdded(bookmarks::BookmarkModel * model,const bookmarks::BookmarkNode * parent,size_t index)219 void DeclarativeContentIsBookmarkedConditionTracker::BookmarkNodeAdded(
220 bookmarks::BookmarkModel* model,
221 const bookmarks::BookmarkNode* parent,
222 size_t index) {
223 if (!extensive_bookmark_changes_in_progress_) {
224 for (const auto& web_contents_tracker_pair : per_web_contents_tracker_) {
225 web_contents_tracker_pair.second->BookmarkAddedForUrl(
226 parent->children()[index]->url());
227 }
228 }
229 }
230
BookmarkNodeRemoved(bookmarks::BookmarkModel * model,const bookmarks::BookmarkNode * parent,size_t old_index,const bookmarks::BookmarkNode * node,const std::set<GURL> & no_longer_bookmarked)231 void DeclarativeContentIsBookmarkedConditionTracker::BookmarkNodeRemoved(
232 bookmarks::BookmarkModel* model,
233 const bookmarks::BookmarkNode* parent,
234 size_t old_index,
235 const bookmarks::BookmarkNode* node,
236 const std::set<GURL>& no_longer_bookmarked) {
237 if (!extensive_bookmark_changes_in_progress_) {
238 for (const auto& web_contents_tracker_pair : per_web_contents_tracker_) {
239 web_contents_tracker_pair.second->BookmarkRemovedForUrls(
240 no_longer_bookmarked);
241 }
242 }
243 }
244
245 void DeclarativeContentIsBookmarkedConditionTracker::
ExtensiveBookmarkChangesBeginning(bookmarks::BookmarkModel * model)246 ExtensiveBookmarkChangesBeginning(
247 bookmarks::BookmarkModel* model) {
248 ++extensive_bookmark_changes_in_progress_;
249 }
250
251 void
ExtensiveBookmarkChangesEnded(bookmarks::BookmarkModel * model)252 DeclarativeContentIsBookmarkedConditionTracker::ExtensiveBookmarkChangesEnded(
253 bookmarks::BookmarkModel* model) {
254 if (--extensive_bookmark_changes_in_progress_ == 0)
255 UpdateAllPerWebContentsTrackers();
256 }
257
258 void
GroupedBookmarkChangesBeginning(bookmarks::BookmarkModel * model)259 DeclarativeContentIsBookmarkedConditionTracker::GroupedBookmarkChangesBeginning(
260 bookmarks::BookmarkModel* model) {
261 ++extensive_bookmark_changes_in_progress_;
262 }
263
264 void
GroupedBookmarkChangesEnded(bookmarks::BookmarkModel * model)265 DeclarativeContentIsBookmarkedConditionTracker::GroupedBookmarkChangesEnded(
266 bookmarks::BookmarkModel* model) {
267 if (--extensive_bookmark_changes_in_progress_ == 0)
268 UpdateAllPerWebContentsTrackers();
269 }
270
271 void
DeletePerWebContentsTracker(content::WebContents * contents)272 DeclarativeContentIsBookmarkedConditionTracker::DeletePerWebContentsTracker(
273 content::WebContents* contents) {
274 DCHECK(base::Contains(per_web_contents_tracker_, contents));
275 per_web_contents_tracker_.erase(contents);
276 }
277
278 void DeclarativeContentIsBookmarkedConditionTracker::
UpdateAllPerWebContentsTrackers()279 UpdateAllPerWebContentsTrackers() {
280 for (const auto& web_contents_tracker_pair : per_web_contents_tracker_)
281 web_contents_tracker_pair.second->UpdateState(false);
282 }
283
284 } // namespace extensions
285