// Copyright 2014 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 "chrome/browser/search/suggestions/suggestions_source.h" #include #include "base/barrier_closure.h" #include "base/base64.h" #include "base/bind.h" #include "base/memory/ref_counted_memory.h" #include "base/strings/string16.h" #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search/instant_io_context.h" #include "chrome/browser/search/suggestions/suggestions_service.h" #include "chrome/browser/search/suggestions/suggestions_service_factory.h" #include "chrome/common/url_constants.h" #include "net/base/escape.h" #include "net/url_request/url_request.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/image/image_skia.h" #include "url/gurl.h" namespace suggestions { namespace { const char kHtmlHeader[] = "\n\n\nSuggestions\n" "\n" "\n"; const char kHtmlBody[] = "\n\n"; const char kHtmlFooter[] = "\n\n"; // Fills |output| with the HTML needed to display the suggestions. void RenderOutputHtml(const SuggestionsProfile& profile, const std::map& base64_encoded_pngs, std::string* output) { std::vector out; out.push_back(kHtmlHeader); out.push_back(kHtmlBody); out.push_back("

Suggestions

\n"); out.push_back(kHtmlFooter); *output = JoinString(out, ""); } // Fills |output| with the HTML needed to display that no suggestions are // available. void RenderOutputHtmlNoSuggestions(std::string* output) { std::vector out; out.push_back(kHtmlHeader); out.push_back(kHtmlBody); out.push_back("

Suggestions

\n"); out.push_back("

You have no suggestions.

\n"); out.push_back(kHtmlFooter); *output = JoinString(out, ""); } } // namespace SuggestionsSource::SuggestionsSource(Profile* profile) : profile_(profile), weak_ptr_factory_(this) {} SuggestionsSource::~SuggestionsSource() {} SuggestionsSource::RequestContext::RequestContext( const SuggestionsProfile& suggestions_profile_in, const content::URLDataSource::GotDataCallback& callback_in) : suggestions_profile(suggestions_profile_in), // Copy. callback(callback_in) // Copy. {} SuggestionsSource::RequestContext::~RequestContext() {} std::string SuggestionsSource::GetSource() const { return chrome::kChromeUISuggestionsHost; } void SuggestionsSource::StartDataRequest( const std::string& path, int render_process_id, int render_frame_id, const content::URLDataSource::GotDataCallback& callback) { SuggestionsServiceFactory* suggestions_service_factory = SuggestionsServiceFactory::GetInstance(); SuggestionsService* suggestions_service( suggestions_service_factory->GetForProfile(profile_)); if (!suggestions_service) { callback.Run(NULL); return; } suggestions_service->FetchSuggestionsData( base::Bind(&SuggestionsSource::OnSuggestionsAvailable, weak_ptr_factory_.GetWeakPtr(), callback)); } std::string SuggestionsSource::GetMimeType(const std::string& path) const { return "text/html"; } base::MessageLoop* SuggestionsSource::MessageLoopForRequestPath( const std::string& path) const { // This can be accessed from the IO thread. return content::URLDataSource::MessageLoopForRequestPath(path); } bool SuggestionsSource::ShouldServiceRequest( const net::URLRequest* request) const { if (request->url().SchemeIs(chrome::kChromeSearchScheme)) return InstantIOContext::ShouldServiceRequest(request); return URLDataSource::ShouldServiceRequest(request); } void SuggestionsSource::OnSuggestionsAvailable( const content::URLDataSource::GotDataCallback& callback, const SuggestionsProfile& suggestions_profile) { size_t size = suggestions_profile.suggestions_size(); if (!size) { std::string output; RenderOutputHtmlNoSuggestions(&output); callback.Run(base::RefCountedString::TakeString(&output)); } else { RequestContext* context = new RequestContext(suggestions_profile, callback); base::Closure barrier = BarrierClosure( size, base::Bind(&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. SuggestionsService* suggestions_service( SuggestionsServiceFactory::GetForProfile(profile_)); suggestions_service->GetPageThumbnail( GURL(suggestion.url()), base::Bind(&SuggestionsSource::OnThumbnailAvailable, weak_ptr_factory_.GetWeakPtr(), context, barrier)); } } } void SuggestionsSource::OnThumbnailsFetched(RequestContext* context) { scoped_ptr context_deleter(context); std::string output; RenderOutputHtml(context->suggestions_profile, context->base64_encoded_pngs, &output); context->callback.Run(base::RefCountedString::TakeString(&output)); } void SuggestionsSource::OnThumbnailAvailable(RequestContext* context, base::Closure barrier, const GURL& url, const SkBitmap* bitmap) { if (bitmap) { std::vector output; gfx::PNGCodec::EncodeBGRASkBitmap(*bitmap, false, &output); std::string encoded_output; base::Base64Encode(std::string(output.begin(), output.end()), &encoded_output); context->base64_encoded_pngs[url] = "data:image/png;base64,"; context->base64_encoded_pngs[url] += encoded_output; } barrier.Run(); } } // namespace suggestions