// Copyright (c) 2006-2008 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. #ifndef CHROME_BROWSER_HISTORY_HISTORY_BACKEND_H__ #define CHROME_BROWSER_HISTORY_HISTORY_BACKEND_H__ #include #include "base/gfx/rect.h" #include "base/lock.h" #include "base/scoped_ptr.h" #include "base/task.h" #include "chrome/browser/history/archived_database.h" #include "chrome/browser/history/download_types.h" #include "chrome/browser/history/expire_history_backend.h" #include "chrome/browser/history/history_database.h" #include "chrome/browser/history/history_marshaling.h" #include "chrome/browser/history/history_types.h" #include "chrome/browser/history/page_usage_data.h" #include "chrome/browser/history/text_database_manager.h" #include "chrome/browser/history/thumbnail_database.h" #include "chrome/browser/history/visit_tracker.h" #include "chrome/common/mru_cache.h" #include "chrome/common/scoped_vector.h" #include "testing/gtest/include/gtest/gtest_prod.h" class BookmarkService; struct ThumbnailScore; namespace history { class CommitLaterTask; // *See the .cc file for more information on the design.* // // Internal history implementation which does most of the work of the history // system. This runs on a background thread (to not block the browser when we // do expensive operations) and is NOT threadsafe, so it must only be called // from message handlers on the background thread. Invoking on another thread // requires threadsafe refcounting. // // Most functions here are just the implementations of the corresponding // functions in the history service. These functions are not documented // here, see the history service for behavior. class HistoryBackend : public base::RefCountedThreadSafe, public BroadcastNotificationDelegate { public: // Interface implemented by the owner of the HistoryBackend object. Normally, // the history service implements this to send stuff back to the main thread. // The unit tests can provide a different implementation if they don't have // a history service object. class Delegate { public: virtual ~Delegate() {} // Called when the database is from a future version of the product and can // not be used. virtual void NotifyTooNew() = 0; // Sets the in-memory history backend. The in-memory backend is created by // the main backend. For non-unit tests, this happens on the background // thread. It is to be used on the main thread, so this would transfer // it to the history service. Unit tests can override this behavior. // // This function is NOT guaranteed to be called. If there is an error, // there may be no in-memory database. // // Ownership of the backend pointer is transferred to this function. virtual void SetInMemoryBackend(InMemoryHistoryBackend* backend) = 0; // Broadcasts the specified notification to the notification service. // This is implemented here because notifications must only be sent from // the main thread. // // Ownership of the HistoryDetails is transferred to this function. virtual void BroadcastNotifications(NotificationType type, HistoryDetails* details) = 0; // Invoked when the backend has finished loading the db. virtual void DBLoaded() = 0; }; // Init must be called to complete object creation. This object can be // constructed on any thread, but all other functions including Init() must // be called on the history thread. // // |history_dir| is the directory where the history files will be placed. // See the definition of BroadcastNotificationsCallback above. This function // takes ownership of the callback pointer. // // |bookmark_service| is used to determine bookmarked URLs when deleting and // may be NULL. // // This constructor is fast and does no I/O, so can be called at any time. HistoryBackend(const std::wstring& history_dir, Delegate* delegate, BookmarkService* bookmark_service); ~HistoryBackend(); // Must be called after creation but before any objects are created. If this // fails, all other functions will fail as well. (Since this runs on another // thread, we don't bother returning failure.) void Init(); // Notification that the history system is shutting down. This will break // the refs owned by the delegate and any pending transaction so it will // actually be deleted. void Closing(); // See NotifyRenderProcessHostDestruction. void NotifyRenderProcessHostDestruction(const void* host); // Navigation ---------------------------------------------------------------- void AddPage(scoped_refptr request); void SetPageTitle(const GURL& url, const std::wstring& title); void AddPageWithDetails(const URLRow& info); // Indexing ------------------------------------------------------------------ void SetPageContents(const GURL& url, const std::wstring& contents); // Querying ------------------------------------------------------------------ // ScheduleAutocomplete() never frees |provider| (which is globally live). // It passes |params| on to the autocomplete system which will eventually // free it. void ScheduleAutocomplete(HistoryURLProvider* provider, HistoryURLProviderParams* params); void IterateURLs(HistoryService::URLEnumerator* enumerator); void QueryURL(scoped_refptr request, const GURL& url, bool want_visits); void QueryHistory(scoped_refptr request, const std::wstring& text_query, const QueryOptions& options); void QueryRedirectsFrom(scoped_refptr request, const GURL& url); void GetVisitCountToHost(scoped_refptr request, const GURL& url); // Computes the most recent URL(s) that the given canonical URL has // redirected to and returns true on success. There may be more than one // redirect in a row, so this function will fill the given array with the // entire chain. If there are no redirects for the most recent visit of the // URL, or the URL is not in history, returns false. // // Backend for QueryRedirectsFrom. bool GetMostRecentRedirectsFrom(const GURL& url, HistoryService::RedirectList* redirects); // Thumbnails ---------------------------------------------------------------- void SetPageThumbnail(const GURL& url, const SkBitmap& thumbnail, const ThumbnailScore& score); // Retrieves a thumbnail, passing it across thread boundaries // via. the included callback. void GetPageThumbnail(scoped_refptr request, const GURL& page_url); // Backend implementation of GetPageThumbnail. Unlike // GetPageThumbnail(), this method has way to transport data across // thread boundaries. // // Exposed for testing reasons. void GetPageThumbnailDirectly( const GURL& page_url, scoped_refptr* data); // Favicon ------------------------------------------------------------------- void GetFavIcon(scoped_refptr request, const GURL& icon_url); void GetFavIconForURL(scoped_refptr request, const GURL& page_url); void SetFavIcon(const GURL& page_url, const GURL& icon_url, scoped_refptr data); void UpdateFavIconMappingAndFetch(scoped_refptr request, const GURL& page_url, const GURL& icon_url); void SetFavIconOutOfDateForPage(const GURL& page_url); void SetImportedFavicons( const std::vector& favicon_usage); // Downloads ----------------------------------------------------------------- void QueryDownloads(scoped_refptr request); void UpdateDownload(int64 received_bytes, int32 state, int64 db_handle); void CreateDownload(scoped_refptr request, const DownloadCreateInfo& info); void RemoveDownload(int64 db_handle); void RemoveDownloadsBetween(const Time remove_begin, const Time remove_end); void RemoveDownloads(const Time remove_end); void SearchDownloads(scoped_refptr, const std::wstring& search_text); // Segment usage ------------------------------------------------------------- void QuerySegmentUsage(scoped_refptr request, const Time from_time); void DeleteOldSegmentData(); void SetSegmentPresentationIndex(SegmentID segment_id, int index); // Keyword search terms ------------------------------------------------------ void SetKeywordSearchTermsForURL(const GURL& url, TemplateURL::IDType keyword_id, const std::wstring& term); void DeleteAllSearchTermsForKeyword(TemplateURL::IDType keyword_id); void GetMostRecentKeywordSearchTerms( scoped_refptr request, TemplateURL::IDType keyword_id, const std::wstring& prefix, int max_count); // Generic operations -------------------------------------------------------- void ProcessDBTask(scoped_refptr request); // Deleting ------------------------------------------------------------------ void DeleteURL(const GURL& url); // Calls ExpireHistoryBackend::ExpireHistoryBetween and commits the change. void ExpireHistoryBetween(scoped_refptr request, Time begin_time, Time end_time); // Bookmarks ----------------------------------------------------------------- // Notification that a URL is no longer bookmarked. If there are no visits // for the specified url, it is deleted. void URLsNoLongerBookmarked(const std::set& urls); // Testing ------------------------------------------------------------------- // Sets the task to run and the message loop to run it on when this object // is destroyed. See HistoryService::SetOnBackendDestroyTask for a more // complete description. void SetOnBackendDestroyTask(MessageLoop* message_loop, Task* task); // Adds the given rows to the database if it doesn't exist. A visit will be // added for each given URL at the last visit time in the URLRow. void AddPagesWithDetails(const std::vector& info); private: friend class CommitLaterTask; // The commit task needs to call Commit(). friend class HistoryTest; // So the unit tests can poke our innards. FRIEND_TEST(HistoryBackendTest, DeleteAll); FRIEND_TEST(HistoryBackendTest, URLsNoLongerBookmarked); friend class TestingProfile; // For invoking methods that circumvent requests. friend class HistoryTest; // Computes the name of the specified database on disk. std::wstring GetThumbnailFileName() const; std::wstring GetArchivedFileName() const; class URLQuerier; friend class URLQuerier; // Does the work of Init. void InitImpl(); // Adds a single visit to the database, updating the URL information such // as visit and typed count. The visit ID of the added visit and the URL ID // of the associated URL (whether added or not) is returned. Both values will // be 0 on failure. // // This does not schedule database commits, it is intended to be used as a // subroutine for AddPage only. It also assumes the database is valid. std::pair AddPageVisit(const GURL& url, Time time, VisitID referring_visit, PageTransition::Type transition); // Returns a redirect chain in |redirects| for the VisitID // |cur_visit|. |cur_visit| is assumed to be valid. Assumes that // this HistoryBackend object has been Init()ed successfully. void GetRedirectsFromSpecificVisit( VisitID cur_visit, HistoryService::RedirectList* redirects); // Thumbnail Helpers --------------------------------------------------------- // When a simple GetMostRecentRedirectsFrom() fails, this method is // called which searches the last N visit sessions instead of just // the current one. Returns true and puts thumbnail data in |data| // if a proper thumbnail was found. Returns false otherwise. Assumes // that this HistoryBackend object has been Init()ed successfully. bool GetThumbnailFromOlderRedirect( const GURL& page_url, std::vector* data); // Querying ------------------------------------------------------------------ // Backends for QueryHistory. *Basic() handles queries that are not FTS (full // text search) queries and can just be given directly to the history DB). // The FTS version queries the text_database, then merges with the history DB. // Both functions assume QueryHistory already checked the DB for validity. void QueryHistoryBasic(URLDatabase* url_db, VisitDatabase* visit_db, const QueryOptions& options, QueryResults* result); void QueryHistoryFTS(const std::wstring& text_query, const QueryOptions& options, QueryResults* result); // Committing ---------------------------------------------------------------- // We always keep a transaction open on the history database so that multiple // transactions can be batched. Periodically, these are flushed (use // ScheduleCommit). This function does the commit to write any new changes to // disk and opens a new transaction. This will be called automatically by // ScheduleCommit, or it can be called explicitly if a caller really wants // to write something to disk. void Commit(); // Schedules a commit to happen in the future. We do this so that many // operations over a period of time will be batched together. If there is // already a commit scheduled for the future, this will do nothing. void ScheduleCommit(); // Cancels the scheduled commit, if any. If there is no scheduled commit, // does nothing. void CancelScheduledCommit(); // Segments ------------------------------------------------------------------ // Walks back a segment chain to find the last visit with a non null segment // id and returns it. If there is none found, returns 0. SegmentID GetLastSegmentID(VisitID from_visit); // Update the segment information. This is called internally when a page is // added. Return the segment id of the segment that has been updated. SegmentID UpdateSegments(const GURL& url, VisitID from_visit, VisitID visit_id, PageTransition::Type transition_type, const Time ts); // Favicons ------------------------------------------------------------------ // Used by both UpdateFavIconMappingAndFetch and GetFavIcon. // If page_url is non-null and SetFavIcon has previously been invoked for // icon_url the favicon url for page_url (and all redirects) is set to // icon_url. void UpdateFavIconMappingAndFetchImpl( const GURL* page_url, const GURL& icon_url, scoped_refptr request); // Sets the favicon url id for page_url to id. This will also broadcast // notifications as necessary. void SetFavIconMapping(const GURL& page_url, FavIconID id); // Generic stuff ------------------------------------------------------------- // Processes the next scheduled HistoryDBTask, scheduling this method // to be invoked again if there are more tasks that need to run. void ProcessDBTaskImpl(); // Release all tasks in history_db_tasks_ and clears it. void ReleaseDBTasks(); // Schedules a broadcast of the given notification on the main thread. The // details argument will have ownership taken by this function (it will be // sent to the main thread and deleted there). void BroadcastNotifications(NotificationType type, HistoryDetails* details_deleted); // Deleting all history ------------------------------------------------------ // Deletes all history. This is a special case of deleting that is separated // from our normal dependency-following method for performance reasons. The // logic lives here instead of ExpireHistoryBackend since it will cause // re-initialization of some databases such as Thumbnails or Archived that // could fail. When these databases are not valid, our pointers must be NULL, // so we need to handle this type of operation to keep the pointers in sync. void DeleteAllHistory(); // Given a vector of all URLs that we will keep, removes all thumbnails // referenced by any URL, and also all favicons that aren't used by those // URLs. The favicon IDs will change, so this will update the url rows in the // vector to reference the new IDs. bool ClearAllThumbnailHistory(std::vector* kept_urls); // Deletes all information in the history database, except for the supplied // set of URLs in the URL table (these should correspond to the bookmarked // URLs). // // The IDs of the URLs may change. bool ClearAllMainHistory(const std::vector& kept_urls); // Returns the BookmarkService, blocking until it is loaded. This may return // NULL during testing. BookmarkService* GetBookmarkService(); // Data ---------------------------------------------------------------------- // Delegate. See the class definition above for more information. This will // be NULL before Init is called and after Cleanup, but is guaranteed // non-NULL in between. scoped_ptr delegate_; // Directory where database files will be stored. std::wstring history_dir_; // The history/thumbnail databases. Either MAY BE NULL if the database could // not be opened, all users must first check for NULL and return immediately // if it is. The thumbnail DB may be NULL when the history one isn't, but not // vice-versa. scoped_ptr db_; scoped_ptr thumbnail_db_; // Stores old history in a larger, slower database. scoped_ptr archived_db_; // Full text database manager, possibly NULL if the database could not be // created. scoped_ptr text_database_; // Manages expiration between the various databases. ExpireHistoryBackend expirer_; // A commit has been scheduled to occur sometime in the future. We can check // non-null-ness to see if there is a commit scheduled in the future, and we // can use the pointer to cancel the scheduled commit. There can be only one // scheduled commit at a time (see ScheduleCommit). scoped_refptr scheduled_commit_; // Maps recent redirect destination pages to the chain of redirects that // brought us to there. Pages that did not have redirects or were not the // final redirect in a chain will not be in this list, as well as pages that // redirected "too long" ago (as determined by ExpireOldRedirects above). // It is used to set titles & favicons for redirects to that of the // destination. // // As with AddPage, the last item in the redirect chain will be the // destination of the redirect (i.e., the key into recent_redirects_); typedef MRUCache RedirectCache; RedirectCache recent_redirects_; // Timestamp of the last page addition request. We use this to detect when // multiple additions are requested at the same time (within the resolution // of the timer), so we can try to ensure they're unique when they're added // to the database by using the last_recorded_time_ (q.v.). We still can't // enforce or guarantee uniqueness, since the user might set his clock back. Time last_requested_time_; // Timestamp of the last page addition, as it was recorded in the database. // If two or more requests come in at the same time, we increment that time // by 1 us between them so it's more likely to be unique in the database. // This keeps track of that higher-resolution timestamp. Time last_recorded_time_; // When non-NULL, this is the task that should be invoked on MessageLoop* backend_destroy_message_loop_; Task* backend_destroy_task_; // Tracks page transition types. VisitTracker tracker_; // A boolean variable to track whether we have already purged obsolete segment // data. bool segment_queried_; // HistoryDBTasks to run. Be sure to AddRef when adding, and Release when // done. std::list db_task_requests_; // Used to determine if a URL is bookmarked. This is owned by the Profile and // may be NULL (during testing). // // Use GetBookmarkService to access this, which makes sure the service is // loaded. BookmarkService* bookmark_service_; DISALLOW_EVIL_CONSTRUCTORS(HistoryBackend); }; } // namespace history #endif // CHROME_BROWSER_HISTORY_HISTORY_BACKEND_H__