summaryrefslogtreecommitdiff
path: root/chromium/components/suggestions
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/suggestions')
-rw-r--r--chromium/components/suggestions/BUILD.gn3
-rw-r--r--chromium/components/suggestions/image_encoder.cc6
-rw-r--r--chromium/components/suggestions/image_manager.cc9
-rw-r--r--chromium/components/suggestions/image_manager.h3
-rw-r--r--chromium/components/suggestions/image_manager_unittest.cc5
-rw-r--r--chromium/components/suggestions/proto/suggestions.proto12
-rw-r--r--chromium/components/suggestions/suggestions_service_impl.cc2
-rw-r--r--chromium/components/suggestions/suggestions_service_impl_unittest.cc2
-rw-r--r--chromium/components/suggestions/webui/suggestions_source.cc197
-rw-r--r--chromium/components/suggestions/webui/suggestions_source.h75
10 files changed, 297 insertions, 17 deletions
diff --git a/chromium/components/suggestions/BUILD.gn b/chromium/components/suggestions/BUILD.gn
index 6d93d045bc4..fa4acf4a65f 100644
--- a/chromium/components/suggestions/BUILD.gn
+++ b/chromium/components/suggestions/BUILD.gn
@@ -18,6 +18,8 @@ static_library("suggestions") {
"suggestions_service_impl.h",
"suggestions_store.cc",
"suggestions_store.h",
+ "webui/suggestions_source.cc",
+ "webui/suggestions_source.h",
]
public_deps = [
@@ -25,6 +27,7 @@ static_library("suggestions") {
"//components/prefs",
"//components/suggestions/proto",
"//net",
+ "//ui/base",
"//ui/gfx",
"//url",
]
diff --git a/chromium/components/suggestions/image_encoder.cc b/chromium/components/suggestions/image_encoder.cc
index 8b0fa09fc26..414766446c4 100644
--- a/chromium/components/suggestions/image_encoder.cc
+++ b/chromium/components/suggestions/image_encoder.cc
@@ -20,10 +20,8 @@ bool EncodeSkBitmapToJPEG(const SkBitmap& bitmap,
if (!bitmap.readyToDraw() || bitmap.isNull()) {
return false;
}
- return gfx::JPEGCodec::Encode(
- reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
- gfx::JPEGCodec::FORMAT_SkBitmap, bitmap.width(), bitmap.height(),
- bitmap.rowBytes(), 100, dest);
+
+ return gfx::JPEGCodec::Encode(bitmap, 100, dest);
}
} // namespace suggestions
diff --git a/chromium/components/suggestions/image_manager.cc b/chromium/components/suggestions/image_manager.cc
index d2fb227f9ac..321aef28ac1 100644
--- a/chromium/components/suggestions/image_manager.cc
+++ b/chromium/components/suggestions/image_manager.cc
@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/task_runner_util.h"
+#include "base/task_scheduler/post_task.h"
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/suggestions/image_encoder.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
@@ -82,11 +83,11 @@ ImageManager::ImageManager() : weak_ptr_factory_(this) {}
ImageManager::ImageManager(
std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
std::unique_ptr<ProtoDatabase<ImageData>> database,
- const base::FilePath& database_dir,
- scoped_refptr<base::TaskRunner> background_task_runner)
+ const base::FilePath& database_dir)
: image_fetcher_(std::move(image_fetcher)),
database_(std::move(database)),
- background_task_runner_(background_task_runner),
+ background_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
+ {base::TaskPriority::USER_VISIBLE})),
database_ready_(false),
weak_ptr_factory_(this) {
image_fetcher_->SetImageFetcherDelegate(this);
@@ -216,6 +217,8 @@ void ImageManager::ServeFromCacheOrNetwork(
void ImageManager::SaveImage(const std::string& url, const SkBitmap& bitmap) {
scoped_refptr<base::RefCountedBytes> encoded_data(
new base::RefCountedBytes());
+ // TODO(treib): Should encoding happen on the |background_task_runner_|?
+ // *De*coding happens there.
if (!EncodeSkBitmapToJPEG(bitmap, &encoded_data->data())) {
return;
}
diff --git a/chromium/components/suggestions/image_manager.h b/chromium/components/suggestions/image_manager.h
index 20de2eea4d3..04ffc002b77 100644
--- a/chromium/components/suggestions/image_manager.h
+++ b/chromium/components/suggestions/image_manager.h
@@ -47,8 +47,7 @@ class ImageManager : public image_fetcher::ImageFetcherDelegate {
ImageManager(
std::unique_ptr<image_fetcher::ImageFetcher> image_fetcher,
std::unique_ptr<leveldb_proto::ProtoDatabase<ImageData>> database,
- const base::FilePath& database_dir,
- scoped_refptr<base::TaskRunner> background_task_runner);
+ const base::FilePath& database_dir);
~ImageManager() override;
virtual void Initialize(const SuggestionsProfile& suggestions);
diff --git a/chromium/components/suggestions/image_manager_unittest.cc b/chromium/components/suggestions/image_manager_unittest.cc
index 86717dd169b..5c42a290d47 100644
--- a/chromium/components/suggestions/image_manager_unittest.cc
+++ b/chromium/components/suggestions/image_manager_unittest.cc
@@ -11,7 +11,6 @@
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/image_fetcher/core/image_fetcher_delegate.h"
#include "components/leveldb_proto/proto_database.h"
@@ -23,7 +22,6 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
using ::testing::Return;
@@ -129,8 +127,7 @@ class ImageManagerTest : public testing::Test {
EXPECT_CALL(*mock_image_fetcher_, SetImageFetcherDelegate(_));
return new ImageManager(base::WrapUnique(mock_image_fetcher_),
base::WrapUnique(fake_db),
- FakeDB<ImageData>::DirectoryForTestDB(),
- base::ThreadTaskRunnerHandle::Get());
+ FakeDB<ImageData>::DirectoryForTestDB());
}
EntryMap db_model_;
diff --git a/chromium/components/suggestions/proto/suggestions.proto b/chromium/components/suggestions/proto/suggestions.proto
index 27f5d141c33..5fcf03ae8ac 100644
--- a/chromium/components/suggestions/proto/suggestions.proto
+++ b/chromium/components/suggestions/proto/suggestions.proto
@@ -23,15 +23,19 @@ enum ProviderId {
//
// Notice that the tags on this proto must match the ones on the server side.
//
-// Next tag: 2
+// Next tag: 17
message SuggestionsProfile {
repeated ChromeSuggestion suggestions = 1;
+ reserved 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15;
+
// Timestamp when the profile was generated (usec).
optional int64 timestamp = 16;
}
-// The suggestions for this user, ordered from best to worst.
+// An individual suggestion.
+//
+// Notice that the tags on this proto must match the ones on the server side.
//
// Next tag: 15
message ChromeSuggestion {
@@ -50,8 +54,12 @@ message ChromeSuggestion {
// The provider(s) responsible for this suggestion.
repeated ProviderId providers = 5;
+ reserved 6;
+
// The timestamp (usec) at which this suggestion ceases to be valid.
optional int64 expiry_ts = 7;
+
+ reserved 8, 9, 10, 11, 12, 13, 14;
}
// A list of URLs that should be filtered from the SuggestionsProfile.
diff --git a/chromium/components/suggestions/suggestions_service_impl.cc b/chromium/components/suggestions/suggestions_service_impl.cc
index aacd400d7b5..e1af0660e44 100644
--- a/chromium/components/suggestions/suggestions_service_impl.cc
+++ b/chromium/components/suggestions/suggestions_service_impl.cc
@@ -92,7 +92,7 @@ const char kSuggestionsBlacklistClearURLFormat[] =
const char kSuggestionsBlacklistURLParam[] = "url";
const char kSuggestionsDeviceParam[] = "t=%s";
-const char kSuggestionsMinParam[] = "min=%i";
+const char kSuggestionsMinParam[] = "num=%i";
const char kSuggestionsMinVariationName[] = "min_suggestions";
const int kSuggestionsMinVariationDefault = 0;
diff --git a/chromium/components/suggestions/suggestions_service_impl_unittest.cc b/chromium/components/suggestions/suggestions_service_impl_unittest.cc
index 7aa45beb51e..90fc4f5959c 100644
--- a/chromium/components/suggestions/suggestions_service_impl_unittest.cc
+++ b/chromium/components/suggestions/suggestions_service_impl_unittest.cc
@@ -343,7 +343,7 @@ TEST_F(SuggestionsServiceTest, BuildUrlWithDefaultMinZeroParamForFewFeature) {
ASSERT_TRUE(GetCurrentlyQueriedUrl().is_valid());
EXPECT_EQ(GetCurrentlyQueriedUrl().path(), kSuggestionsUrlPath);
std::string min_suggestions;
- EXPECT_TRUE(net::GetValueForKeyInQuery(GetCurrentlyQueriedUrl(), "min",
+ EXPECT_TRUE(net::GetValueForKeyInQuery(GetCurrentlyQueriedUrl(), "num",
&min_suggestions));
EXPECT_EQ(min_suggestions, "0");
RespondToFetchWithProfile(CreateSuggestionsProfile());
diff --git a/chromium/components/suggestions/webui/suggestions_source.cc b/chromium/components/suggestions/webui/suggestions_source.cc
new file mode 100644
index 00000000000..ab9b927cd5e
--- /dev/null
+++ b/chromium/components/suggestions/webui/suggestions_source.cc
@@ -0,0 +1,197 @@
+// Copyright 2017 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 "components/suggestions/webui/suggestions_source.h"
+
+#include "base/barrier_closure.h"
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "components/suggestions/proto/suggestions.pb.h"
+#include "net/base/escape.h"
+#include "ui/base/l10n/time_format.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace suggestions {
+
+namespace {
+
+const char kHtmlHeader[] =
+ "<!DOCTYPE html>\n<html>\n<head>\n<title>Suggestions</title>\n"
+ "<meta charset=\"utf-8\">\n"
+ "<style type=\"text/css\">\nli {white-space: nowrap;}\n</style>\n";
+const char kHtmlBody[] = "</head>\n<body>\n";
+const char kHtmlFooter[] = "</body>\n</html>\n";
+
+const char kRefreshPath[] = "refresh";
+
+std::string GetRefreshHtml(const std::string& base_url, bool is_refresh) {
+ if (is_refresh)
+ return "<p>Refreshing in the background, reload to see new data.</p>\n";
+ return std::string("<p><a href=\"") + base_url + kRefreshPath +
+ "\">Refresh</a></p>\n";
+}
+// Returns the HTML needed to display the suggestions.
+std::string RenderOutputHtml(
+ const std::string& base_url,
+ bool is_refresh,
+ const SuggestionsProfile& profile,
+ const std::map<GURL, std::string>& base64_encoded_pngs) {
+ std::vector<std::string> out;
+ out.push_back(kHtmlHeader);
+ out.push_back(kHtmlBody);
+ out.push_back("<h1>Suggestions</h1>\n");
+ out.push_back(GetRefreshHtml(base_url, is_refresh));
+ out.push_back("<ul>");
+ int64_t now = (base::Time::NowFromSystemTime() - base::Time::UnixEpoch())
+ .ToInternalValue();
+ size_t size = profile.suggestions_size();
+ for (size_t i = 0; i < size; ++i) {
+ const ChromeSuggestion& suggestion = profile.suggestions(i);
+ base::TimeDelta remaining_time =
+ base::TimeDelta::FromMicroseconds(suggestion.expiry_ts() - now);
+ base::string16 remaining_time_formatted = ui::TimeFormat::Detailed(
+ ui::TimeFormat::Format::FORMAT_DURATION,
+ ui::TimeFormat::Length::LENGTH_LONG, -1, remaining_time);
+ std::string line;
+ line += "<li><a href=\"";
+ line += net::EscapeForHTML(suggestion.url());
+ line += "\" target=\"_blank\">";
+ line += net::EscapeForHTML(suggestion.title());
+ std::map<GURL, std::string>::const_iterator it =
+ base64_encoded_pngs.find(GURL(suggestion.url()));
+ if (it != base64_encoded_pngs.end()) {
+ line += "<br><img src='";
+ line += it->second;
+ line += "'>";
+ }
+ line += "</a> Expires in ";
+ line += base::UTF16ToUTF8(remaining_time_formatted);
+ std::vector<std::string> providers;
+ for (int p = 0; p < suggestion.providers_size(); ++p)
+ providers.push_back(base::IntToString(suggestion.providers(p)));
+ line += ". Provider IDs: " + base::JoinString(providers, ", ");
+ line += "</li>\n";
+ out.push_back(line);
+ }
+ out.push_back("</ul>");
+ out.push_back(kHtmlFooter);
+ return base::JoinString(out, base::StringPiece());
+}
+
+// Returns the HTML needed to display that no suggestions are available.
+std::string RenderOutputHtmlNoSuggestions(const std::string& base_url,
+ bool is_refresh) {
+ std::vector<std::string> out;
+ out.push_back(kHtmlHeader);
+ out.push_back(kHtmlBody);
+ out.push_back("<h1>Suggestions</h1>\n");
+ out.push_back("<p>You have no suggestions.</p>\n");
+ out.push_back(GetRefreshHtml(base_url, is_refresh));
+ out.push_back(kHtmlFooter);
+ return base::JoinString(out, base::StringPiece());
+}
+
+} // namespace
+
+SuggestionsSource::SuggestionsSource(SuggestionsService* suggestions_service,
+ const std::string& base_url)
+ : suggestions_service_(suggestions_service),
+ base_url_(base_url),
+ weak_ptr_factory_(this) {}
+
+SuggestionsSource::~SuggestionsSource() {}
+
+SuggestionsSource::RequestContext::RequestContext(
+ bool is_refresh_in,
+ const SuggestionsProfile& suggestions_profile_in,
+ const GotDataCallback& callback_in)
+ : is_refresh(is_refresh_in),
+ suggestions_profile(suggestions_profile_in), // Copy.
+ callback(callback_in) // Copy.
+{}
+
+SuggestionsSource::RequestContext::~RequestContext() {}
+
+void SuggestionsSource::StartDataRequest(const std::string& path,
+ const GotDataCallback& callback) {
+ // If this was called as "chrome://suggestions/refresh", we also trigger an
+ // async update of the suggestions.
+ bool is_refresh = (path == kRefreshPath);
+
+ // |suggestions_service| is null for guest profiles.
+ if (!suggestions_service_) {
+ std::string output = RenderOutputHtmlNoSuggestions(base_url_, is_refresh);
+ callback.Run(base::RefCountedString::TakeString(&output));
+ return;
+ }
+
+ if (is_refresh)
+ suggestions_service_->FetchSuggestionsData();
+
+ SuggestionsProfile suggestions_profile =
+ suggestions_service_->GetSuggestionsDataFromCache().value_or(
+ SuggestionsProfile());
+ size_t size = suggestions_profile.suggestions_size();
+ if (!size) {
+ std::string output = RenderOutputHtmlNoSuggestions(base_url_, is_refresh);
+ callback.Run(base::RefCountedString::TakeString(&output));
+ } else {
+ RequestContext* context =
+ new RequestContext(is_refresh, suggestions_profile, callback);
+ base::Closure barrier = BarrierClosure(
+ size, base::BindOnce(&SuggestionsSource::OnThumbnailsFetched,
+ weak_ptr_factory_.GetWeakPtr(), context));
+ for (size_t i = 0; i < size; ++i) {
+ const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i);
+ // Fetch the thumbnail for this URL (exercising the fetcher). After all
+ // fetches are done, including NULL callbacks for unavailable thumbnails,
+ // SuggestionsSource::OnThumbnailsFetched will be called.
+ suggestions_service_->GetPageThumbnail(
+ GURL(suggestion.url()),
+ base::Bind(&SuggestionsSource::OnThumbnailAvailable,
+ weak_ptr_factory_.GetWeakPtr(), context, barrier));
+ }
+ }
+}
+
+std::string SuggestionsSource::GetMimeType(const std::string& path) const {
+ return "text/html";
+}
+
+void SuggestionsSource::OnThumbnailsFetched(RequestContext* context) {
+ std::unique_ptr<RequestContext> context_deleter(context);
+
+ std::string output = RenderOutputHtml(base_url_, context->is_refresh,
+ context->suggestions_profile,
+ context->base64_encoded_pngs);
+ context->callback.Run(base::RefCountedString::TakeString(&output));
+}
+
+void SuggestionsSource::OnThumbnailAvailable(RequestContext* context,
+ const base::Closure& barrier,
+ const GURL& url,
+ const gfx::Image& image) {
+ if (!image.IsEmpty()) {
+ std::vector<unsigned char> output;
+ gfx::PNGCodec::EncodeBGRASkBitmap(*image.ToSkBitmap(), false, &output);
+
+ std::string encoded_output;
+ base::Base64Encode(
+ base::StringPiece(reinterpret_cast<const char*>(output.data()),
+ output.size()),
+ &encoded_output);
+ context->base64_encoded_pngs[url] = "data:image/png;base64,";
+ context->base64_encoded_pngs[url] += encoded_output;
+ }
+ barrier.Run();
+}
+
+} // namespace suggestions
diff --git a/chromium/components/suggestions/webui/suggestions_source.h b/chromium/components/suggestions/webui/suggestions_source.h
new file mode 100644
index 00000000000..c5c2b4f9c38
--- /dev/null
+++ b/chromium/components/suggestions/webui/suggestions_source.h
@@ -0,0 +1,75 @@
+// Copyright 2017 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 COMPONENTS_SUGGESTIONS_WEBUI_SUGGESTIONS_SOURCE_H_
+#define COMPONENTS_SUGGESTIONS_WEBUI_SUGGESTIONS_SOURCE_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/weak_ptr.h"
+#include "components/suggestions/suggestions_service.h"
+#include "ui/gfx/image/image.h"
+#include "url/gurl.h"
+
+namespace suggestions {
+
+// SuggestionsSource renders a webpage to list SuggestionsService data.
+class SuggestionsSource {
+ public:
+ SuggestionsSource(SuggestionsService* suggestions_service,
+ const std::string& base_url);
+ ~SuggestionsSource();
+
+ using GotDataCallback =
+ base::Callback<void(scoped_refptr<base::RefCountedMemory>)>;
+
+ void StartDataRequest(const std::string& path,
+ const GotDataCallback& callback);
+ std::string GetMimeType(const std::string& path) const;
+
+ private:
+ // Container for the state of a request.
+ struct RequestContext {
+ RequestContext(
+ bool is_refresh_in,
+ const suggestions::SuggestionsProfile& suggestions_profile_in,
+ const GotDataCallback& callback_in);
+ ~RequestContext();
+
+ const bool is_refresh;
+ const suggestions::SuggestionsProfile suggestions_profile;
+ const GotDataCallback callback;
+ std::map<GURL, std::string> base64_encoded_pngs;
+ };
+
+ // Callback for responses from each Thumbnail request.
+ void OnThumbnailAvailable(RequestContext* context,
+ const base::Closure& barrier,
+ const GURL& url,
+ const gfx::Image& image);
+
+ // Callback for when all requests are complete. Renders the output webpage and
+ // passes the result to the original caller.
+ void OnThumbnailsFetched(RequestContext* context);
+
+ // Only used when servicing requests on the UI thread.
+ SuggestionsService* suggestions_service_;
+
+ // The base URL at which which the Suggestions WebUI lives in the context of
+ // the embedder.
+ const std::string base_url_;
+
+ // For callbacks may be run after destruction.
+ base::WeakPtrFactory<SuggestionsSource> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(SuggestionsSource);
+};
+
+} // namespace suggestions
+
+#endif // COMPONENTS_SUGGESTIONS_WEBUI_SUGGESTIONS_SOURCE_H_