1// Copyright (c) 2012 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#ifndef CHROME_BROWSER_HISTORY_SHORTCUTS_BACKEND_H_
6#define CHROME_BROWSER_HISTORY_SHORTCUTS_BACKEND_H_
7
8#include <map>
9#include <string>
10#include <vector>
11
12#include "base/files/file_path.h"
13#include "base/gtest_prod_util.h"
14#include "base/memory/ref_counted.h"
15#include "base/memory/scoped_ptr.h"
16#include "base/observer_list.h"
17#include "base/strings/string16.h"
18#include "base/synchronization/lock.h"
19#include "base/time/time.h"
20#include "chrome/browser/autocomplete/autocomplete_match.h"
21#include "components/browser_context_keyed_service/refcounted_browser_context_keyed_service.h"
22#include "content/public/browser/notification_observer.h"
23#include "content/public/browser/notification_registrar.h"
24#include "url/gurl.h"
25
26class Profile;
27
28namespace history {
29
30class ShortcutsDatabase;
31
32// This class manages the shortcut provider backend - access to database on the
33// db thread, etc.
34class ShortcutsBackend : public RefcountedBrowserContextKeyedService,
35                         public content::NotificationObserver {
36 public:
37  // The following struct encapsulates one previously selected omnibox shortcut.
38  struct Shortcut {
39    // The pieces of an AutocompleteMatch that we preserve in a shortcut.
40    struct MatchCore {
41      explicit MatchCore(const AutocompleteMatch& match);
42      MatchCore(const base::string16& fill_into_edit,
43                const GURL& destination_url,
44                const base::string16& contents,
45                const ACMatchClassifications& contents_class,
46                const base::string16& description,
47                const ACMatchClassifications& description_class,
48                content::PageTransition transition,
49                AutocompleteMatch::Type type,
50                const base::string16& keyword);
51      ~MatchCore();
52
53      AutocompleteMatch ToMatch() const;
54
55      base::string16 fill_into_edit;
56      GURL destination_url;
57      base::string16 contents;
58      // For both contents_class and description_class, we strip MATCH
59      // classifications; the ShortcutsProvider will re-mark MATCH regions based
60      // on the user's current typing.
61      ACMatchClassifications contents_class;
62      base::string16 description;
63      ACMatchClassifications description_class;
64      content::PageTransition transition;
65      AutocompleteMatch::Type type;
66      base::string16 keyword;
67    };
68
69    Shortcut(const std::string& id,
70             const base::string16& text,
71             const MatchCore& match_core,
72             const base::Time& last_access_time,
73             int number_of_hits);
74    // Required for STL, we don't use this directly.
75    Shortcut();
76    ~Shortcut();
77
78    std::string id;  // Unique guid for the shortcut.
79    base::string16 text;   // The user's original input string.
80    MatchCore match_core;
81    base::Time last_access_time;  // Last time shortcut was selected.
82    int number_of_hits;           // How many times shortcut was selected.
83  };
84
85  typedef std::multimap<base::string16, const Shortcut> ShortcutMap;
86
87  // |profile| is necessary for profile notifications only and can be NULL in
88  // unit-tests. For unit testing, set |suppress_db| to true to prevent creation
89  // of the database, in which case all operations are performed in memory only.
90  ShortcutsBackend(Profile* profile, bool suppress_db);
91
92  // The interface is guaranteed to be called on the thread AddObserver()
93  // was called.
94  class ShortcutsBackendObserver {
95   public:
96    // Called after the database is loaded and Init() completed.
97    virtual void OnShortcutsLoaded() = 0;
98    // Called when shortcuts changed (added/updated/removed) in the database.
99    virtual void OnShortcutsChanged() {}
100
101   protected:
102    virtual ~ShortcutsBackendObserver() {}
103  };
104
105  // Asynchronously initializes the ShortcutsBackend, it is safe to call
106  // multiple times - only the first call will be processed.
107  bool Init();
108
109  // All of the public functions *must* be called on UI thread only!
110
111  bool initialized() const { return current_state_ == INITIALIZED; }
112  const ShortcutMap& shortcuts_map() const { return shortcuts_map_; }
113
114  // Deletes the Shortcuts with the url.
115  bool DeleteShortcutsWithUrl(const GURL& shortcut_url);
116
117  void AddObserver(ShortcutsBackendObserver* obs);
118  void RemoveObserver(ShortcutsBackendObserver* obs);
119
120  // Looks for an existing shortcut to match.destination_url that starts with
121  // |text|.  Updates that shortcut if found, otherwise adds a new shortcut.
122  void AddOrUpdateShortcut(const base::string16& text,
123                           const AutocompleteMatch& match);
124
125 private:
126  friend class base::RefCountedThreadSafe<ShortcutsBackend>;
127  friend class ShortcutsProviderTest;
128  FRIEND_TEST_ALL_PREFIXES(ShortcutsBackendTest, AddAndUpdateShortcut);
129  FRIEND_TEST_ALL_PREFIXES(ShortcutsBackendTest, DeleteShortcuts);
130
131  enum CurrentState {
132    NOT_INITIALIZED,  // Backend created but not initialized.
133    INITIALIZING,     // Init() called, but not completed yet.
134    INITIALIZED,      // Initialization completed, all accessors can be safely
135                      // called.
136  };
137
138  typedef std::map<std::string, ShortcutMap::iterator> GuidMap;
139
140  virtual ~ShortcutsBackend();
141
142  // RefcountedBrowserContextKeyedService:
143  virtual void ShutdownOnUIThread() OVERRIDE;
144
145  // content::NotificationObserver:
146  virtual void Observe(int type,
147                       const content::NotificationSource& source,
148                       const content::NotificationDetails& details) OVERRIDE;
149
150  // Internal initialization of the back-end. Posted by Init() to the DB thread.
151  // On completion posts InitCompleted() back to UI thread.
152  void InitInternal();
153
154  // Finishes initialization on UI thread, notifies all observers.
155  void InitCompleted();
156
157  // Adds the Shortcut to the database.
158  bool AddShortcut(const Shortcut& shortcut);
159
160  // Updates timing and selection count for the Shortcut.
161  bool UpdateShortcut(const Shortcut& shortcut);
162
163  // Deletes the Shortcuts with the id.
164  bool DeleteShortcutsWithIds(const std::vector<std::string>& shortcut_ids);
165
166  // Deletes all shortcuts whose URLs begin with |url|.  If |exact_match| is
167  // true, only shortcuts from exactly |url| are deleted.
168  bool DeleteShortcutsWithUrl(const GURL& url, bool exact_match);
169
170  // Deletes all of the shortcuts.
171  bool DeleteAllShortcuts();
172
173  CurrentState current_state_;
174  ObserverList<ShortcutsBackendObserver> observer_list_;
175  scoped_refptr<ShortcutsDatabase> db_;
176
177  // The |temp_shortcuts_map_| and |temp_guid_map_| used for temporary storage
178  // between InitInternal() and InitComplete() to avoid doing a potentially huge
179  // copy.
180  scoped_ptr<ShortcutMap> temp_shortcuts_map_;
181  scoped_ptr<GuidMap> temp_guid_map_;
182
183  ShortcutMap shortcuts_map_;
184  // This is a helper map for quick access to a shortcut by guid.
185  GuidMap guid_map_;
186
187  content::NotificationRegistrar notification_registrar_;
188
189  // For some unit-test only.
190  bool no_db_access_;
191
192  DISALLOW_COPY_AND_ASSIGN(ShortcutsBackend);
193};
194
195}  // namespace history
196
197#endif  // CHROME_BROWSER_HISTORY_SHORTCUTS_BACKEND_H_
198