| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/extensions/api/declarative_content/declarative_content_is_bookmarked_condition_tracker.h" |
| |
| #include <memory> |
| #include <set> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/stl_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/values_test_util.h" |
| #include "chrome/browser/bookmarks/bookmark_model_factory.h" |
| #include "chrome/browser/extensions/api/declarative_content/content_predicate_evaluator.h" |
| #include "chrome/browser/extensions/api/declarative_content/declarative_content_condition_tracker_test.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/bookmarks/browser/bookmark_model.h" |
| #include "components/bookmarks/browser/scoped_group_bookmark_actions.h" |
| #include "components/bookmarks/test/bookmark_test_helpers.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/web_contents.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_builder.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| |
| namespace extensions { |
| |
| namespace { |
| |
| scoped_refptr<const Extension> CreateExtensionWithBookmarksPermission( |
| bool include_bookmarks) { |
| ListBuilder permissions; |
| permissions.Append("declarativeContent"); |
| if (include_bookmarks) |
| permissions.Append("bookmarks"); |
| return ExtensionBuilder() |
| .SetManifest(DictionaryBuilder() |
| .Set("name", "Test extension") |
| .Set("version", "1.0") |
| .Set("manifest_version", 2) |
| .Set("permissions", permissions.Build()) |
| .Build()) |
| .Build(); |
| } |
| |
| std::unique_ptr<DeclarativeContentIsBookmarkedPredicate> CreatePredicate( |
| ContentPredicateEvaluator* evaluator, |
| const Extension* extension, |
| bool is_bookmarked) { |
| std::string error; |
| std::unique_ptr<DeclarativeContentIsBookmarkedPredicate> predicate = |
| DeclarativeContentIsBookmarkedPredicate::Create( |
| evaluator, extension, |
| *base::test::ParseJson(is_bookmarked ? "true" : "false"), &error); |
| EXPECT_EQ("", error); |
| EXPECT_TRUE(predicate); |
| EXPECT_EQ(is_bookmarked, predicate->is_bookmarked()); |
| return predicate; |
| } |
| |
| } // namespace |
| |
| using testing::HasSubstr; |
| using testing::UnorderedElementsAre; |
| using testing::UnorderedElementsAreArray; |
| |
| class DeclarativeContentIsBookmarkedConditionTrackerTest |
| : public DeclarativeContentConditionTrackerTest { |
| protected: |
| class Delegate : public ContentPredicateEvaluator::Delegate { |
| public: |
| Delegate() {} |
| |
| std::set<content::WebContents*>& evaluation_requests() { |
| return evaluation_requests_; |
| } |
| |
| // ContentPredicateEvaluator::Delegate: |
| void RequestEvaluation(content::WebContents* contents) override { |
| EXPECT_FALSE(base::ContainsKey(evaluation_requests_, contents)); |
| evaluation_requests_.insert(contents); |
| } |
| |
| bool ShouldManageConditionsForBrowserContext( |
| content::BrowserContext* context) override { |
| return true; |
| } |
| |
| private: |
| std::set<content::WebContents*> evaluation_requests_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Delegate); |
| }; |
| |
| DeclarativeContentIsBookmarkedConditionTrackerTest() { |
| profile()->CreateBookmarkModel(true); |
| bookmarks::test::WaitForBookmarkModelToLoad( |
| BookmarkModelFactory::GetForBrowserContext(profile())); |
| bookmark_model_ = BookmarkModelFactory::GetForBrowserContext(profile()); |
| tracker_.reset(new DeclarativeContentIsBookmarkedConditionTracker( |
| profile(), |
| &delegate_)); |
| extension_ = CreateExtensionWithBookmarksPermission(true); |
| is_bookmarked_predicate_ = CreatePredicate(tracker_.get(), extension_.get(), |
| true); |
| is_not_bookmarked_predicate_ = CreatePredicate(tracker_.get(), |
| extension_.get(), false); |
| } |
| |
| void LoadURL(content::WebContents* tab, const GURL& url) { |
| tab->GetController().LoadURL(url, content::Referrer(), |
| ui::PAGE_TRANSITION_LINK, std::string()); |
| } |
| |
| testing::AssertionResult CheckPredicates(content::WebContents* tab, |
| bool page_is_bookmarked) { |
| const bool is_bookmarked_predicate_success = |
| page_is_bookmarked == |
| tracker_->EvaluatePredicate(is_bookmarked_predicate_.get(), tab); |
| const bool is_not_bookmarked_predicate_success = |
| page_is_bookmarked != |
| tracker_->EvaluatePredicate(is_not_bookmarked_predicate_.get(), tab); |
| |
| if (is_bookmarked_predicate_success && is_not_bookmarked_predicate_success) |
| return testing::AssertionSuccess(); |
| |
| testing::AssertionResult result = testing::AssertionFailure(); |
| if (!is_bookmarked_predicate_success) { |
| result << "IsBookmarkedPredicate(true): expected " |
| << (page_is_bookmarked ? "true" : "false") << " got " |
| << (page_is_bookmarked ? "false" : "true"); |
| } |
| |
| if (!is_not_bookmarked_predicate_success) { |
| if (!is_bookmarked_predicate_success) |
| result << "; "; |
| result << "IsBookmarkedPredicate(false): expected " |
| << (page_is_bookmarked ? "false" : "true") << " got " |
| << (page_is_bookmarked ? "true" : "false"); |
| } |
| |
| return result; |
| } |
| |
| Delegate delegate_; |
| bookmarks::BookmarkModel* bookmark_model_; |
| std::unique_ptr<DeclarativeContentIsBookmarkedConditionTracker> tracker_; |
| scoped_refptr<const Extension> extension_; |
| std::unique_ptr<DeclarativeContentIsBookmarkedPredicate> |
| is_bookmarked_predicate_; |
| std::unique_ptr<DeclarativeContentIsBookmarkedPredicate> |
| is_not_bookmarked_predicate_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(DeclarativeContentIsBookmarkedConditionTrackerTest); |
| }; |
| |
| |
| // Tests that condition with isBookmarked requires "bookmarks" permission. |
| TEST(DeclarativeContentIsBookmarkedPredicateTest, |
| IsBookmarkedPredicateRequiresBookmarkPermissionPermission) { |
| scoped_refptr<const Extension> extension = |
| CreateExtensionWithBookmarksPermission(false); |
| std::string error; |
| std::unique_ptr<DeclarativeContentIsBookmarkedPredicate> predicate = |
| DeclarativeContentIsBookmarkedPredicate::Create( |
| nullptr, extension.get(), *base::test::ParseJson("true"), &error); |
| EXPECT_THAT(error, HasSubstr("requires 'bookmarks' permission")); |
| EXPECT_FALSE(predicate); |
| } |
| |
| // Tests an invalid isBookmarked value type. |
| TEST(DeclarativeContentIsBookmarkedPredicateTest, |
| WrongIsBookmarkedPredicateDatatype) { |
| scoped_refptr<const Extension> extension = |
| CreateExtensionWithBookmarksPermission(true); |
| std::string error; |
| std::unique_ptr<DeclarativeContentIsBookmarkedPredicate> predicate = |
| DeclarativeContentIsBookmarkedPredicate::Create( |
| nullptr, extension.get(), *base::test::ParseJson("[]"), &error); |
| EXPECT_THAT(error, HasSubstr("invalid type")); |
| EXPECT_FALSE(predicate); |
| } |
| |
| // Tests isBookmark: true. Predicate state is checked in CreatePredicate(). |
| TEST(DeclarativeContentIsBookmarkedPredicateTest, IsBookmarkedPredicateTrue) { |
| scoped_refptr<const Extension> extension = |
| CreateExtensionWithBookmarksPermission(true); |
| std::unique_ptr<DeclarativeContentIsBookmarkedPredicate> predicate = |
| CreatePredicate(nullptr, extension.get(), true); |
| } |
| |
| // Tests isBookmark: false. Predicate state is checked in CreatePredicate(). |
| TEST(DeclarativeContentIsBookmarkedPredicateTest, IsBookmarkedPredicateFalse) { |
| scoped_refptr<const Extension> extension = |
| CreateExtensionWithBookmarksPermission(true); |
| std::unique_ptr<DeclarativeContentIsBookmarkedPredicate> predicate = |
| CreatePredicate(nullptr, extension.get(), false); |
| } |
| |
| // Tests that starting tracking for a WebContents that has a bookmarked URL |
| // results in the proper IsUrlBookmarked state. |
| TEST_F(DeclarativeContentIsBookmarkedConditionTrackerTest, |
| BookmarkedAtStartOfTracking) { |
| std::unique_ptr<content::WebContents> tab = MakeTab(); |
| LoadURL(tab.get(), GURL("http://bookmarked/")); |
| EXPECT_TRUE(delegate_.evaluation_requests().empty()); |
| |
| bookmark_model_->AddURL(bookmark_model_->other_node(), 0, |
| base::ASCIIToUTF16("title"), |
| GURL("http://bookmarked/")); |
| |
| tracker_->TrackForWebContents(tab.get()); |
| EXPECT_THAT(delegate_.evaluation_requests(), UnorderedElementsAre(tab.get())); |
| EXPECT_TRUE(CheckPredicates(tab.get(), true)); |
| } |
| |
| // Tests that adding and removing bookmarks triggers evaluation requests for the |
| // matching WebContents. |
| TEST_F(DeclarativeContentIsBookmarkedConditionTrackerTest, |
| AddAndRemoveBookmark) { |
| // Create two tabs. |
| std::vector<std::unique_ptr<content::WebContents>> tabs; |
| for (int i = 0; i < 2; ++i) { |
| tabs.push_back(MakeTab()); |
| delegate_.evaluation_requests().clear(); |
| tracker_->TrackForWebContents(tabs.back().get()); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs.back().get())); |
| EXPECT_TRUE(CheckPredicates(tabs.back().get(), false)); |
| } |
| |
| // Navigate the first tab to a URL that we will bookmark. |
| delegate_.evaluation_requests().clear(); |
| LoadURL(tabs[0].get(), GURL("http://bookmarked/")); |
| tracker_->OnWebContentsNavigation(tabs[0].get(), nullptr); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), false)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| |
| // Bookmark the first tab's URL. |
| delegate_.evaluation_requests().clear(); |
| const bookmarks::BookmarkNode* node = |
| bookmark_model_->AddURL(bookmark_model_->other_node(), 0, |
| base::ASCIIToUTF16("title"), |
| GURL("http://bookmarked/")); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), true)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| |
| // Remove the bookmark. |
| delegate_.evaluation_requests().clear(); |
| bookmark_model_->Remove(node); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), false)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| } |
| |
| // Tests that adding and removing bookmarks triggers evaluation requests for the |
| // matching WebContents. |
| TEST_F(DeclarativeContentIsBookmarkedConditionTrackerTest, ExtensiveChanges) { |
| // Create two tabs. |
| std::vector<std::unique_ptr<content::WebContents>> tabs; |
| for (int i = 0; i < 2; ++i) { |
| tabs.push_back(MakeTab()); |
| delegate_.evaluation_requests().clear(); |
| tracker_->TrackForWebContents(tabs.back().get()); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs.back().get())); |
| EXPECT_TRUE(CheckPredicates(tabs.back().get(), false)); |
| } |
| |
| // Navigate the first tab to a URL that we will bookmark. |
| delegate_.evaluation_requests().clear(); |
| LoadURL(tabs[0].get(), GURL("http://bookmarked/")); |
| tracker_->OnWebContentsNavigation(tabs[0].get(), nullptr); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), false)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| |
| { |
| // Check that evaluation requests occur outside ExtensiveBookmarkChanges for |
| // added nodes. |
| delegate_.evaluation_requests().clear(); |
| bookmark_model_->BeginExtensiveChanges(); |
| const bookmarks::BookmarkNode* node = |
| bookmark_model_->AddURL(bookmark_model_->other_node(), 0, |
| base::ASCIIToUTF16("title"), |
| GURL("http://bookmarked/")); |
| EXPECT_TRUE(delegate_.evaluation_requests().empty()); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), false)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| bookmark_model_->EndExtensiveChanges(); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), true)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| |
| // Check that evaluation requests occur outside ExtensiveBookmarkChanges for |
| // removed nodes. |
| delegate_.evaluation_requests().clear(); |
| bookmark_model_->BeginExtensiveChanges(); |
| bookmark_model_->Remove(node); |
| EXPECT_TRUE(delegate_.evaluation_requests().empty()); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), true)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| bookmark_model_->EndExtensiveChanges(); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), false)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| } |
| |
| { |
| // Check that evaluation requests occur outside ScopedGroupBookmarkActions |
| // for added nodes. |
| delegate_.evaluation_requests().clear(); |
| const bookmarks::BookmarkNode* node = nullptr; |
| { |
| bookmarks::ScopedGroupBookmarkActions scoped_group(bookmark_model_); |
| node = bookmark_model_->AddURL(bookmark_model_->other_node(), 0, |
| base::ASCIIToUTF16("title"), |
| GURL("http://bookmarked/")); |
| EXPECT_TRUE(delegate_.evaluation_requests().empty()); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), false)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| } |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), true)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| |
| // Check that evaluation requests occur outside ScopedGroupBookmarkActions |
| // for removed nodes. |
| delegate_.evaluation_requests().clear(); |
| { |
| bookmarks::ScopedGroupBookmarkActions scoped_group(bookmark_model_); |
| bookmark_model_->Remove(node); |
| EXPECT_TRUE(delegate_.evaluation_requests().empty()); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), true)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| } |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), false)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| } |
| } |
| |
| // Tests that navigation to bookmarked and non-bookmarked URLs triggers |
| // evaluation requests for the relevant WebContents. |
| TEST_F(DeclarativeContentIsBookmarkedConditionTrackerTest, Navigation) { |
| // Bookmark two URLs. |
| delegate_.evaluation_requests().clear(); |
| bookmark_model_->AddURL(bookmark_model_->other_node(), 0, |
| base::ASCIIToUTF16("title"), |
| GURL("http://bookmarked1/")); |
| bookmark_model_->AddURL(bookmark_model_->other_node(), 0, |
| base::ASCIIToUTF16("title"), |
| GURL("http://bookmarked2/")); |
| |
| // Create two tabs. |
| std::vector<std::unique_ptr<content::WebContents>> tabs; |
| for (int i = 0; i < 2; ++i) { |
| tabs.push_back(MakeTab()); |
| delegate_.evaluation_requests().clear(); |
| tracker_->TrackForWebContents(tabs.back().get()); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs.back().get())); |
| EXPECT_TRUE(CheckPredicates(tabs.back().get(), false)); |
| } |
| |
| // Navigate the first tab to one bookmarked URL. |
| delegate_.evaluation_requests().clear(); |
| LoadURL(tabs[0].get(), GURL("http://bookmarked1/")); |
| tracker_->OnWebContentsNavigation(tabs[0].get(), nullptr); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), true)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| |
| // Navigate the first tab to another bookmarked URL. The contents have |
| // changed, so we should receive a new evaluation request even though the |
| // bookmarked state hasn't. |
| delegate_.evaluation_requests().clear(); |
| LoadURL(tabs[0].get(), GURL("http://bookmarked2/")); |
| tracker_->OnWebContentsNavigation(tabs[0].get(), nullptr); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), true)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| |
| // Navigate the first tab to a non-bookmarked URL. |
| delegate_.evaluation_requests().clear(); |
| LoadURL(tabs[0].get(), GURL("http://not-bookmarked1/")); |
| tracker_->OnWebContentsNavigation(tabs[0].get(), nullptr); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), false)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| |
| // Navigate the first tab to another non-bookmarked URL. The contents have |
| // changed, so we should receive a new evaluation request even though the |
| // bookmarked state hasn't. |
| delegate_.evaluation_requests().clear(); |
| LoadURL(tabs[0].get(), GURL("http://not-bookmarked2/")); |
| tracker_->OnWebContentsNavigation(tabs[0].get(), nullptr); |
| EXPECT_THAT(delegate_.evaluation_requests(), |
| UnorderedElementsAre(tabs[0].get())); |
| EXPECT_TRUE(CheckPredicates(tabs[0].get(), false)); |
| EXPECT_TRUE(CheckPredicates(tabs[1].get(), false)); |
| } |
| |
| } // namespace extensions |