| // Copyright 2016 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. |
| |
| // This file contains the top-level class for the RulesetService. There are |
| // associated classes that tie this into the dealer as well as the filter |
| // agents. The distribution pipeline looks like this: |
| // |
| // RulesetService |
| // | |
| // v Browser |
| // RulesetPublisher(Impl) |
| // | | |
| // - - - - - - -|- - - - - - - |- - - - - - - - - - |
| // | | | |
| // v v |
| // *RulesetDealer | *RulesetDealer |
| // | | | |
| // | | | v |
| // v | SubresourceFilterAgent |
| // SubresourceFilterAgent | v |
| // SubresourceFilterAgent |
| // | |
| // |
| // Renderer #1 | Renderer #n |
| // |
| // Note: UnverifiedRulesetDealer is shortened to *RulesetDealer above. There is |
| // also a VerifiedRulesetDealer which is used similarly on the browser side. |
| |
| #ifndef COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_RULESET_SERVICE_H_ |
| #define COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_RULESET_SERVICE_H_ |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/callback.h" |
| #include "base/files/file.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/version.h" |
| #include "components/subresource_filter/content/browser/ruleset_publisher.h" |
| #include "components/subresource_filter/content/browser/ruleset_version.h" |
| #include "components/subresource_filter/content/browser/verified_ruleset_dealer.h" |
| #include "content/public/browser/notification_observer.h" |
| #include "content/public/browser/notification_registrar.h" |
| |
| namespace base { |
| class SequencedTaskRunner; |
| } // namespace base |
| |
| namespace subresource_filter { |
| |
| class RulesetIndexer; |
| |
| // Contains all utility functions that govern how files pertaining to indexed |
| // ruleset version should be organized on disk. |
| // |
| // The various indexed ruleset versions are kept in a two-level directory |
| // hierarchy based on their format and content version numbers, like so: |
| // |
| // |base_dir| |
| // | |
| // +--10 (format_version) |
| // | | |
| // | +--1 (content_version) |
| // | | \... |
| // | | |
| // | +--2 (content_version) |
| // | \... |
| // | |
| // +--11 (format_version) |
| // | |
| // +--2 (content_version) |
| // \... |
| // |
| class IndexedRulesetLocator { |
| public: |
| // Returns a path to a directory under |base_dir| where files corresponding to |
| // the given |version| should be stored. |
| static base::FilePath GetSubdirectoryPathForVersion( |
| const base::FilePath& base_dir, |
| const IndexedRulesetVersion& version); |
| |
| static base::FilePath GetRulesetDataFilePath( |
| const base::FilePath& version_directory); |
| static base::FilePath GetLicenseFilePath( |
| const base::FilePath& version_directory); |
| static base::FilePath GetSentinelFilePath( |
| const base::FilePath& version_directory); |
| |
| // Cleans up the |indexed_ruleset_base_dir| by deleting all obsoleted ruleset |
| // versions, keeping only: |
| // -- the |most_recent_version|, if it is valid, |
| // -- versions of the current format that have a sentinel file present. |
| // To be called on the |background_task_runner_|. |
| static void DeleteObsoleteRulesets( |
| const base::FilePath& indexed_ruleset_base_dir, |
| const IndexedRulesetVersion& most_recent_version); |
| }; |
| |
| // Responsible for indexing subresource filtering rules that are downloaded |
| // through the component updater; for versioned storage of the indexed ruleset; |
| // and for supplying the most up-to-date version of the indexed ruleset to the |
| // RulesetPublisher, provided in the constructor, that abstracts away |
| // distribution of the ruleset to renderers. |
| // |
| // Files corresponding to each version of the indexed ruleset are stored in a |
| // separate subdirectory inside |indexed_ruleset_base_dir| named after the |
| // version. The version information of the most recent successfully stored |
| // ruleset is written into |local_state|. The invariant is maintained that the |
| // version pointed to by preferences, if valid, will exist on disk at any point |
| // in time. |
| // |
| // Obsolete files deletion and rulesets indexing are posted to |
| // |background_task_runner|. |
| class RulesetService : public base::SupportsWeakPtr<RulesetService> { |
| public: |
| // Enumerates the possible outcomes of indexing a ruleset and writing it to |
| // disk. Used in UMA histograms, so the order of enumerators should not be |
| // changed. |
| enum class IndexAndWriteRulesetResult { |
| SUCCESS, |
| FAILED_CREATING_SCRATCH_DIR, |
| FAILED_WRITING_RULESET_DATA, |
| FAILED_WRITING_LICENSE, |
| FAILED_REPLACE_FILE, |
| FAILED_DELETE_PREEXISTING, |
| FAILED_OPENING_UNINDEXED_RULESET, |
| FAILED_PARSING_UNINDEXED_RULESET, |
| FAILED_CREATING_VERSION_DIR, |
| FAILED_CREATING_SENTINEL_FILE, |
| FAILED_DELETING_SENTINEL_FILE, |
| ABORTED_BECAUSE_SENTINEL_FILE_PRESENT, |
| |
| // Insert new values before this line. |
| MAX, |
| }; |
| |
| // Creates a new instance of a ruleset. This is then assigned to a |
| // RulesetPublisher that calls Initialize for this ruleset service. |
| // Starts initialization of the RulesetService, performing tasks that won't |
| // slow down Chrome startup, then queues the FinishInitialization task. |
| RulesetService( |
| PrefService* local_state, |
| scoped_refptr<base::SequencedTaskRunner> background_task_runner, |
| const base::FilePath& indexed_ruleset_base_dir, |
| scoped_refptr<base::SequencedTaskRunner> blocking_task_runner, |
| // Note: Optional publisher parameter used exclusively for testing. |
| std::unique_ptr<RulesetPublisher> publisher = nullptr); |
| virtual ~RulesetService(); |
| |
| // Pass-through function to set the callback on publishing. |
| void SetRulesetPublishedCallbackForTesting(base::OnceClosure callback) { |
| publisher_->SetRulesetPublishedCallbackForTesting(std::move(callback)); |
| } |
| |
| // Indexes, stores, and publishes the given unindexed ruleset, unless its |
| // |content_version| matches that of the most recently indexed version, in |
| // which case it does nothing. The files comprising the unindexed ruleset |
| // need to remain accessible even after the method returns. |
| // |
| // Computation-heavy steps and I/O are performed on a background thread. |
| // Furthermore, to prevent start-up congestion, new rulesets provided via this |
| // method will not be processed until after start-up. |
| // |
| // Virtual so that it can be mocked out in tests. |
| virtual void IndexAndStoreAndPublishRulesetIfNeeded( |
| const UnindexedRulesetInfo& unindexed_ruleset_info); |
| |
| // Get the ruleset version associated with the current local_state_. |
| IndexedRulesetVersion GetMostRecentlyIndexedVersion() const; |
| |
| VerifiedRulesetDealer::Handle* GetRulesetDealer() { |
| return publisher_->GetRulesetDealer(); |
| } |
| |
| private: |
| friend class SubresourceFilteringRulesetServiceTest; |
| friend class SubresourceFilterBrowserTest; |
| FRIEND_TEST_ALL_PREFIXES( |
| SubresourceFilterRulesetPublisherImplTest, |
| PublishedRuleset_IsDistributedToExistingAndNewRenderers); |
| FRIEND_TEST_ALL_PREFIXES(SubresourceFilterRulesetPublisherImplTest, |
| PublishesRulesetInOnePostTask); |
| FRIEND_TEST_ALL_PREFIXES(SubresourceFilteringRulesetServiceTest, |
| NewRuleset_WriteFailure); |
| FRIEND_TEST_ALL_PREFIXES(SubresourceFilteringRulesetServiceDeathTest, |
| NewRuleset_IndexingCrash); |
| |
| using WriteRulesetCallback = |
| base::OnceCallback<void(const IndexedRulesetVersion&)>; |
| |
| // Reads the ruleset described in |unindexed_ruleset_info|, indexes it, and |
| // calls WriteRuleset() to persist the indexed ruleset. Returns the resulting |
| // indexed ruleset version, or an invalid version on error. To be called on |
| // the |background_task_runner|. |
| static IndexedRulesetVersion IndexAndWriteRuleset( |
| const base::FilePath& indexed_ruleset_base_dir, |
| const UnindexedRulesetInfo& unindexed_ruleset_info); |
| |
| // Reads the rules from the |unindexed_ruleset_file|, and indexes them using |
| // |indexer|. Returns whether the entire ruleset could be parsed. |
| static bool IndexRuleset(base::File unindexed_ruleset_file, |
| RulesetIndexer* indexer); |
| |
| // Writes all files comprising the given |indexed_version| of the ruleset |
| // into the corresponding subdirectory in |indexed_ruleset_base_dir|. |
| // More specifically, it writes: |
| // -- the |indexed_ruleset_data| of the given |indexed_ruleset_size|, |
| // -- a copy of the LICENSE file at |license_path|, if exists. |
| // Returns true on success. To be called on the |background_task_runner|. |
| // Attempts not to leave an incomplete copy in the target directory. |
| // |
| // Writing is factored out into this separate function so it can be |
| // independently exercised in tests. |
| static IndexAndWriteRulesetResult WriteRuleset( |
| const base::FilePath& indexed_ruleset_version_dir, |
| const base::FilePath& license_source_path, |
| const uint8_t* indexed_ruleset_data, |
| size_t indexed_ruleset_size); |
| |
| // Indirections for accessing these routines, so as to allow overriding and |
| // injecting faults in tests. |
| static decltype(&IndexRuleset) g_index_ruleset_func; |
| static decltype(&base::ReplaceFile) g_replace_file_func; |
| |
| // Runs as a BEST_EFFORT task to complete portions of the initialization that |
| // could potentially block Chrome startup. Once this task is reached, the |
| // RulesetService is considered initialized. |
| void FinishInitialization(); |
| |
| // Posts a task to the |background_task_runner| to index and persist the |
| // given unindexed ruleset. Then, on success, updates the most recently |
| // indexed version in preferences and invokes |success_callback| on the |
| // calling thread. There is no callback on failure. |
| void IndexAndStoreRuleset(const UnindexedRulesetInfo& unindexed_ruleset_info, |
| WriteRulesetCallback success_callback); |
| |
| void OnWrittenRuleset(WriteRulesetCallback result_callback, |
| const IndexedRulesetVersion& version); |
| |
| void OpenAndPublishRuleset(const IndexedRulesetVersion& version); |
| void OnRulesetSet(base::File file); |
| |
| PrefService* const local_state_; |
| |
| // Obsolete files deletion and indexing should be done on this runner. |
| scoped_refptr<base::SequencedTaskRunner> background_task_runner_; |
| |
| std::unique_ptr<RulesetPublisher> publisher_; |
| |
| UnindexedRulesetInfo queued_unindexed_ruleset_info_; |
| bool is_initialized_; |
| |
| const base::FilePath indexed_ruleset_base_dir_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RulesetService); |
| }; |
| |
| } // namespace subresource_filter |
| |
| #endif // COMPONENTS_SUBRESOURCE_FILTER_CONTENT_BROWSER_RULESET_SERVICE_H_ |