Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(745)

Side by Side Diff: components/ntp_snippets/remote/remote_suggestions_provider_impl.h

Issue 2557363002: [NTP Snippets] Refactor background scheduling for remote suggestions (Closed)
Patch Set: Changes to make unit-tests pass Created 3 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_H_ 5 #ifndef COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_IMPL_H_
6 #define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_H_ 6 #define COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_IMPL_H_
7 7
8 #include <cstddef> 8 #include <cstddef>
9 #include <deque> 9 #include <deque>
10 #include <map> 10 #include <map>
11 #include <memory> 11 #include <memory>
12 #include <set> 12 #include <set>
13 #include <string> 13 #include <string>
14 #include <utility> 14 #include <utility>
15 #include <vector> 15 #include <vector>
16 16
17 #include "base/callback_forward.h" 17 #include "base/callback_forward.h"
18 #include "base/gtest_prod_util.h" 18 #include "base/gtest_prod_util.h"
19 #include "base/macros.h" 19 #include "base/macros.h"
20 #include "base/time/clock.h" 20 #include "base/time/clock.h"
21 #include "base/time/time.h" 21 #include "base/time/time.h"
22 #include "components/image_fetcher/image_fetcher_delegate.h" 22 #include "components/image_fetcher/image_fetcher_delegate.h"
23 #include "components/ntp_snippets/category.h" 23 #include "components/ntp_snippets/category.h"
24 #include "components/ntp_snippets/category_status.h" 24 #include "components/ntp_snippets/category_status.h"
25 #include "components/ntp_snippets/content_suggestion.h" 25 #include "components/ntp_snippets/content_suggestion.h"
26 #include "components/ntp_snippets/content_suggestions_provider.h" 26 #include "components/ntp_snippets/content_suggestions_provider.h"
27 #include "components/ntp_snippets/remote/ntp_snippet.h" 27 #include "components/ntp_snippets/remote/ntp_snippet.h"
28 #include "components/ntp_snippets/remote/ntp_snippets_fetcher.h" 28 #include "components/ntp_snippets/remote/ntp_snippets_fetcher.h"
29 #include "components/ntp_snippets/remote/ntp_snippets_scheduler.h" 29 #include "components/ntp_snippets/remote/remote_suggestions_provider.h"
30 #include "components/ntp_snippets/remote/remote_suggestions_status_service.h" 30 #include "components/ntp_snippets/remote/remote_suggestions_status_service.h"
31 #include "components/ntp_snippets/remote/request_throttler.h" 31 #include "components/ntp_snippets/remote/request_throttler.h"
32 32
33 class PrefRegistrySimple; 33 class PrefRegistrySimple;
34 class PrefService; 34 class PrefService;
35 35
36 namespace gfx { 36 namespace gfx {
37 class Image; 37 class Image;
38 } // namespace gfx 38 } // namespace gfx
39 39
40 namespace image_fetcher { 40 namespace image_fetcher {
41 class ImageDecoder; 41 class ImageDecoder;
42 class ImageFetcher; 42 class ImageFetcher;
43 } // namespace image_fetcher 43 } // namespace image_fetcher
44 44
45 namespace ntp_snippets { 45 namespace ntp_snippets {
46 46
47 class CategoryRanker;
47 class RemoteSuggestionsDatabase; 48 class RemoteSuggestionsDatabase;
48 class CategoryRanker;
49 class UserClassifier;
50 49
51 // CachedImageFetcher takes care of fetching images from the network and caching 50 // CachedImageFetcher takes care of fetching images from the network and caching
52 // them in the database. 51 // them in the database.
53 // TODO(tschumann): Move into a separate library and inject the 52 // TODO(tschumann): Move into a separate library and inject the
54 // CachedImageFetcher into the RemoteSuggestionsProvider. This allows us to get 53 // CachedImageFetcher into the RemoteSuggestionsProvider. This allows us to get
55 // rid of exposing this member for testing and lets us test the caching logic 54 // rid of exposing this member for testing and lets us test the caching logic
56 // separately. 55 // separately.
57 class CachedImageFetcher : public image_fetcher::ImageFetcherDelegate { 56 class CachedImageFetcher : public image_fetcher::ImageFetcherDelegate {
58 public: 57 public:
59 // |pref_service| and |database| need to outlive the created image fetcher 58 // |pref_service| and |database| need to outlive the created image fetcher
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 RequestThrottler thumbnail_requests_throttler_; 98 RequestThrottler thumbnail_requests_throttler_;
100 99
101 DISALLOW_COPY_AND_ASSIGN(CachedImageFetcher); 100 DISALLOW_COPY_AND_ASSIGN(CachedImageFetcher);
102 }; 101 };
103 102
104 // Retrieves fresh content data (articles) from the server, stores them and 103 // Retrieves fresh content data (articles) from the server, stores them and
105 // provides them as content suggestions. 104 // provides them as content suggestions.
106 // This class is final because it does things in its constructor which make it 105 // This class is final because it does things in its constructor which make it
107 // unsafe to derive from it. 106 // unsafe to derive from it.
108 // TODO(treib): Introduce two-phase initialization and make the class not final? 107 // TODO(treib): Introduce two-phase initialization and make the class not final?
109 class RemoteSuggestionsProvider final : public ContentSuggestionsProvider { 108 class RemoteSuggestionsProviderImpl final : public RemoteSuggestionsProvider {
110 public: 109 public:
111 // |application_language_code| should be a ISO 639-1 compliant string, e.g. 110 // |application_language_code| should be a ISO 639-1 compliant string, e.g.
112 // 'en' or 'en-US'. Note that this code should only specify the language, not 111 // 'en' or 'en-US'. Note that this code should only specify the language, not
113 // the locale, so 'en_US' (English language with US locale) and 'en-GB_US' 112 // the locale, so 'en_US' (English language with US locale) and 'en-GB_US'
114 // (British English person in the US) are not language codes. 113 // (British English person in the US) are not language codes.
115 RemoteSuggestionsProvider( 114 RemoteSuggestionsProviderImpl(
116 Observer* observer, 115 Observer* observer,
117 PrefService* pref_service, 116 PrefService* pref_service,
118 const std::string& application_language_code, 117 const std::string& application_language_code,
119 CategoryRanker* category_ranker, 118 CategoryRanker* category_ranker,
120 const UserClassifier* user_classifier,
121 NTPSnippetsScheduler* scheduler,
122 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, 119 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher,
123 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher, 120 std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
124 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder, 121 std::unique_ptr<image_fetcher::ImageDecoder> image_decoder,
125 std::unique_ptr<RemoteSuggestionsDatabase> database, 122 std::unique_ptr<RemoteSuggestionsDatabase> database,
126 std::unique_ptr<RemoteSuggestionsStatusService> status_service); 123 std::unique_ptr<RemoteSuggestionsStatusService> status_service);
127 124
128 ~RemoteSuggestionsProvider() override; 125 ~RemoteSuggestionsProviderImpl() override;
129 126
130 static void RegisterProfilePrefs(PrefRegistrySimple* registry); 127 static void RegisterProfilePrefs(PrefRegistrySimple* registry);
131 128
132 // Returns whether the service is ready. While this is false, the list of 129 // Returns whether the service is ready. While this is false, the list of
133 // snippets will be empty, and all modifications to it (fetch, dismiss, etc) 130 // snippets will be empty, and all modifications to it (fetch, dismiss, etc)
134 // will be ignored. 131 // will be ignored.
135 bool ready() const { return state_ == State::READY; } 132 bool ready() const { return state_ == State::READY; }
136 133
137 // Returns whether the service is initialized. While this is false, some 134 // Returns whether the service is successfully initialized. While this is
138 // calls may trigger DCHECKs. 135 // false, some calls may trigger DCHECKs.
139 bool initialized() const { return ready() || state_ == State::DISABLED; } 136 bool initialized() const { return ready() || state_ == State::DISABLED; }
140 137
141 // Fetchs content suggestions from the Content Suggestions server for all 138 // RemoteSuggestionsProvider implementation.
142 // remote categories in the background. 139 void SetProviderStatusCallback(
143 void FetchSnippetsInTheBackground(); 140 std::unique_ptr<ProviderStatusCallback> callback) override;
141 void RefetchInTheBackground(
142 std::unique_ptr<FetchStatusCallback> callback) override;
143 const NTPSnippetsFetcher* snippets_fetcher_for_testing_and_debugging()
144 const override;
144 145
145 // Fetchs content suggestions from the Content Suggestions server for all 146 // ContentSuggestionsProvider implementation.
146 // remote categories. The request to the server is performed as an interactive
147 // request. Interactive requests are used for actions triggered by the user
148 // and request lower latency processing.
149 void FetchSnippetsForAllCategories();
150
151 const NTPSnippetsFetcher* snippets_fetcher() const {
152 return snippets_fetcher_.get();
153 }
154
155 // (Re)schedules the periodic fetching of snippets. If |force| is true, the
156 // tasks will be re-scheduled even if they already exist and have the correct
157 // periods.
158 void RescheduleFetching(bool force);
159
160 // ContentSuggestionsProvider implementation
161 CategoryStatus GetCategoryStatus(Category category) override; 147 CategoryStatus GetCategoryStatus(Category category) override;
162 CategoryInfo GetCategoryInfo(Category category) override; 148 CategoryInfo GetCategoryInfo(Category category) override;
163 void DismissSuggestion(const ContentSuggestion::ID& suggestion_id) override; 149 void DismissSuggestion(const ContentSuggestion::ID& suggestion_id) override;
164 void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id, 150 void FetchSuggestionImage(const ContentSuggestion::ID& suggestion_id,
165 const ImageFetchedCallback& callback) override; 151 const ImageFetchedCallback& callback) override;
166 void Fetch(const Category& category, 152 void Fetch(const Category& category,
167 const std::set<std::string>& known_suggestion_ids, 153 const std::set<std::string>& known_suggestion_ids,
168 const FetchDoneCallback& callback) override; 154 const FetchDoneCallback& callback) override;
155 void ReloadSuggestions() override;
169 void ClearHistory( 156 void ClearHistory(
170 base::Time begin, 157 base::Time begin,
171 base::Time end, 158 base::Time end,
172 const base::Callback<bool(const GURL& url)>& filter) override; 159 const base::Callback<bool(const GURL& url)>& filter) override;
173 void ClearCachedSuggestions(Category category) override; 160 void ClearCachedSuggestions(Category category) override;
174 void OnSignInStateChanged() override; 161 void OnSignInStateChanged() override;
175 void GetDismissedSuggestionsForDebugging( 162 void GetDismissedSuggestionsForDebugging(
176 Category category, 163 Category category,
177 const DismissedSuggestionsCallback& callback) override; 164 const DismissedSuggestionsCallback& callback) override;
178 void ClearDismissedSuggestionsForDebugging(Category category) override; 165 void ClearDismissedSuggestionsForDebugging(Category category) override;
(...skipping 16 matching lines...) Expand all
195 // Overrides internal clock for testing purposes. 182 // Overrides internal clock for testing purposes.
196 void SetClockForTesting(std::unique_ptr<base::Clock> clock) { 183 void SetClockForTesting(std::unique_ptr<base::Clock> clock) {
197 clock_ = std::move(clock); 184 clock_ = std::move(clock);
198 } 185 }
199 186
200 // TODO(tschumann): remove this method as soon as we inject the fetcher into 187 // TODO(tschumann): remove this method as soon as we inject the fetcher into
201 // the constructor. 188 // the constructor.
202 CachedImageFetcher& GetImageFetcherForTesting() { return image_fetcher_; } 189 CachedImageFetcher& GetImageFetcherForTesting() { return image_fetcher_; }
203 190
204 private: 191 private:
205 friend class RemoteSuggestionsProviderTest; 192 friend class RemoteSuggestionsProviderImplTest;
206 193
207 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest, 194 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
195 CallsProviderStatusCallbackWhenReady);
196 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
197 CallsProviderStatusCallbackOnError);
198 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
199 CallsProviderStatusCallbackWhenDisabled);
200 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
201 ShouldNotCrashWhenCallingEmptyCallback);
202 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
208 DontNotifyIfNotAvailable); 203 DontNotifyIfNotAvailable);
209 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest, 204 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
210 RemoveExpiredDismissedContent); 205 RemoveExpiredDismissedContent);
211 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest, 206 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest, StatusChanges);
212 RescheduleOnStateChange); 207 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderImplTest,
213 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest, StatusChanges);
214 FRIEND_TEST_ALL_PREFIXES(RemoteSuggestionsProviderTest,
215 SuggestionsFetchedOnSignInAndSignOut); 208 SuggestionsFetchedOnSignInAndSignOut);
216 209
217 // Possible state transitions: 210 // Possible state transitions:
218 // NOT_INITED --------+ 211 // NOT_INITED --------+
219 // / \ | 212 // / \ |
220 // v v | 213 // v v |
221 // READY <--> DISABLED | 214 // READY <--> DISABLED |
222 // \ / | 215 // \ / |
223 // v v | 216 // v v |
224 // ERROR_OCCURRED <-----+ 217 // ERROR_OCCURRED <-----+
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
285 278
286 explicit CategoryContent(const CategoryInfo& info); 279 explicit CategoryContent(const CategoryInfo& info);
287 CategoryContent(CategoryContent&&); 280 CategoryContent(CategoryContent&&);
288 ~CategoryContent(); 281 ~CategoryContent();
289 CategoryContent& operator=(CategoryContent&&); 282 CategoryContent& operator=(CategoryContent&&);
290 }; 283 };
291 284
292 // Fetches snippets from the server and replaces old snippets by the new ones. 285 // Fetches snippets from the server and replaces old snippets by the new ones.
293 // Requests can be marked more important by setting |interactive_request| to 286 // Requests can be marked more important by setting |interactive_request| to
294 // true (such request might circumvent the daily quota for requests, etc.) 287 // true (such request might circumvent the daily quota for requests, etc.)
295 // Useful for requests triggered by the user. 288 // Useful for requests triggered by the user. After the fetch finished, the
296 void FetchSnippets(bool interactive_request); 289 // provided |callback| will be triggered with the status of the fetch.
290 void FetchSnippets(bool interactive_request,
291 std::unique_ptr<FetchStatusCallback> callback);
297 292
298 // Returns the URL of the image of a snippet if it is among the current or 293 // Returns the URL of the image of a snippet if it is among the current or
299 // among the archived snippets in the matching category. Returns an empty URL 294 // among the archived snippets in the matching category. Returns an empty URL
300 // otherwise. 295 // otherwise.
301 GURL FindSnippetImageUrl(const ContentSuggestion::ID& suggestion_id) const; 296 GURL FindSnippetImageUrl(const ContentSuggestion::ID& suggestion_id) const;
302 297
303 // Callbacks for the RemoteSuggestionsDatabase. 298 // Callbacks for the RemoteSuggestionsDatabase.
304 void OnDatabaseLoaded(NTPSnippet::PtrVector snippets); 299 void OnDatabaseLoaded(NTPSnippet::PtrVector snippets);
305 void OnDatabaseError(); 300 void OnDatabaseError();
306 301
307 // Callback for fetch-more requests with the NTPSnippetsFetcher. 302 // Callback for fetch-more requests with the NTPSnippetsFetcher.
308 void OnFetchMoreFinished( 303 void OnFetchMoreFinished(
309 const FetchDoneCallback& fetching_callback, 304 const FetchDoneCallback& fetching_callback,
310 Status status, 305 Status status,
311 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories); 306 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories);
312 307
313 // Callback for regular fetch requests with the NTPSnippetsFetcher. 308 // Callback for regular fetch requests with the NTPSnippetsFetcher.
314 void OnFetchFinished( 309 void OnFetchFinished(
310 std::unique_ptr<FetchStatusCallback> callback,
315 bool interactive_request, 311 bool interactive_request,
316 Status status, 312 Status status,
317 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories); 313 NTPSnippetsFetcher::OptionalFetchedCategories fetched_categories);
318 314
319 // Moves all snippets from |to_archive| into the archive of the |content|. 315 // Moves all snippets from |to_archive| into the archive of the |content|.
320 // Clears |to_archive|. As the archive is a FIFO buffer of limited size, this 316 // Clears |to_archive|. As the archive is a FIFO buffer of limited size, this
321 // function will also delete images from the database in case the associated 317 // function will also delete images from the database in case the associated
322 // snippet gets evicted from the archive. 318 // snippet gets evicted from the archive.
323 void ArchiveSnippets(CategoryContent* content, 319 void ArchiveSnippets(CategoryContent* content,
324 NTPSnippet::PtrVector* to_archive); 320 NTPSnippet::PtrVector* to_archive);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
357 // Triggers a state transition depending on the provided status. This method 353 // Triggers a state transition depending on the provided status. This method
358 // is called when a change is detected by |status_service_|. 354 // is called when a change is detected by |status_service_|.
359 void OnStatusChanged(RemoteSuggestionsStatus old_status, 355 void OnStatusChanged(RemoteSuggestionsStatus old_status,
360 RemoteSuggestionsStatus new_status); 356 RemoteSuggestionsStatus new_status);
361 357
362 // Verifies state transitions (see |State|'s documentation) and applies them. 358 // Verifies state transitions (see |State|'s documentation) and applies them.
363 // Also updates the provider status. Does nothing except updating the provider 359 // Also updates the provider status. Does nothing except updating the provider
364 // status if called with the current state. 360 // status if called with the current state.
365 void EnterState(State state); 361 void EnterState(State state);
366 362
363 // Notifies the state change to ProviderStatusCallback specified by
364 // SetProviderStatusCallback().
365 void NotifyStateChanged();
366
367 // Enables the service. Do not call directly, use |EnterState| instead. 367 // Enables the service. Do not call directly, use |EnterState| instead.
368 void EnterStateReady(); 368 void EnterStateReady();
369 369
370 // Disables the service. Do not call directly, use |EnterState| instead. 370 // Disables the service. Do not call directly, use |EnterState| instead.
371 void EnterStateDisabled(); 371 void EnterStateDisabled();
372 372
373 // Disables the service permanently because an unrecoverable error occurred. 373 // Disables the service permanently because an unrecoverable error occurred.
374 // Do not call directly, use |EnterState| instead. 374 // Do not call directly, use |EnterState| instead.
375 void EnterStateError(); 375 void EnterStateError();
376 376
(...skipping 27 matching lines...) Expand all
404 const Category articles_category_; 404 const Category articles_category_;
405 405
406 std::map<Category, CategoryContent, Category::CompareByID> category_contents_; 406 std::map<Category, CategoryContent, Category::CompareByID> category_contents_;
407 407
408 // The ISO 639-1 code of the language used by the application. 408 // The ISO 639-1 code of the language used by the application.
409 const std::string application_language_code_; 409 const std::string application_language_code_;
410 410
411 // Ranker that orders the categories. Not owned. 411 // Ranker that orders the categories. Not owned.
412 CategoryRanker* category_ranker_; 412 CategoryRanker* category_ranker_;
413 413
414 // Classifier that tells us how active the user is. Not owned.
415 const UserClassifier* user_classifier_;
416
417 // Scheduler for fetching snippets. Not owned.
418 NTPSnippetsScheduler* scheduler_;
419
420 // The snippets fetcher. 414 // The snippets fetcher.
421 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher_; 415 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher_;
422 416
423 // The database for persisting snippets. 417 // The database for persisting snippets.
424 std::unique_ptr<RemoteSuggestionsDatabase> database_; 418 std::unique_ptr<RemoteSuggestionsDatabase> database_;
425 base::TimeTicks database_load_start_; 419 base::TimeTicks database_load_start_;
426 420
427 // The image fetcher. 421 // The image fetcher.
428 CachedImageFetcher image_fetcher_; 422 CachedImageFetcher image_fetcher_;
429 423
430 // The service that provides events and data about the signin and sync state. 424 // The service that provides events and data about the signin and sync state.
431 std::unique_ptr<RemoteSuggestionsStatusService> status_service_; 425 std::unique_ptr<RemoteSuggestionsStatusService> status_service_;
432 426
433 // Set to true if FetchSnippets is called while the service isn't ready. 427 // Set to true if FetchSnippets is called while the service isn't ready.
434 // The fetch will be executed once the service enters the READY state. 428 // The fetch will be executed once the service enters the READY state.
429 // TODO(jkrcal): create a struct and have here just one base::Optional<>?
435 bool fetch_when_ready_; 430 bool fetch_when_ready_;
431 // The parameters for the fetch to perform later.
432 bool fetch_when_ready_interactive_;
433 std::unique_ptr<FetchStatusCallback> fetch_when_ready_callback_;
434
435 std::unique_ptr<ProviderStatusCallback> provider_status_callback_;
436 436
437 // Set to true if NukeAllSnippets is called while the service isn't ready. 437 // Set to true if NukeAllSnippets is called while the service isn't ready.
438 // The nuke will be executed once the service finishes initialization or 438 // The nuke will be executed once the service finishes initialization or
439 // enters the READY state. 439 // enters the READY state.
440 bool nuke_when_initialized_; 440 bool nuke_when_initialized_;
441 441
442 // A clock for getting the time. This allows to inject a clock in tests. 442 // A clock for getting the time. This allows to inject a clock in tests.
443 std::unique_ptr<base::Clock> clock_; 443 std::unique_ptr<base::Clock> clock_;
444 444
445 DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProvider); 445 DISALLOW_COPY_AND_ASSIGN(RemoteSuggestionsProviderImpl);
446 }; 446 };
447 447
448 } // namespace ntp_snippets 448 } // namespace ntp_snippets
449 449
450 #endif // COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_H_ 450 #endif // COMPONENTS_NTP_SNIPPETS_REMOTE_REMOTE_SUGGESTIONS_PROVIDER_IMPL_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698