suggestions_source.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// found in the LICENSE file. 4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/search/suggestions/suggestions_source.h" 6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <vector> 8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/barrier_closure.h" 10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/base64.h" 11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/bind.h" 12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/memory/ref_counted_memory.h" 13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/strings/string16.h" 14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/strings/string_piece.h" 15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/strings/string_util.h" 16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/profiles/profile.h" 17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/search/instant_io_context.h" 18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/search/suggestions/suggestions_service.h" 19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/browser/search/suggestions/suggestions_service_factory.h" 20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/common/url_constants.h" 21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "net/base/escape.h" 22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "net/url_request/url_request.h" 23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "ui/gfx/codec/png_codec.h" 24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "ui/gfx/image/image_skia.h" 25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "url/gurl.h" 26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace suggestions { 28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace { 30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const char kHtmlHeader[] = 32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) "<!DOCTYPE html>\n<html>\n<head>\n<title>Suggestions</title>\n" 33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) "<meta charset=\"utf-8\">\n" 34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) "<style type=\"text/css\">\nli {white-space: nowrap;}\n</style>\n"; 35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const char kHtmlBody[] = "</head>\n<body>\n"; 36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const char kHtmlFooter[] = "</body>\n</html>\n"; 37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Fills |output| with the HTML needed to display the suggestions. 39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void RenderOutputHtml(const SuggestionsProfile& profile, 40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const std::map<GURL, std::string>& base64_encoded_pngs, 41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) std::string* output) { 42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) std::vector<std::string> out; 43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back(kHtmlHeader); 44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back(kHtmlBody); 45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back("<h1>Suggestions</h1>\n<ul>"); 46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) size_t size = profile.suggestions_size(); 48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) for (size_t i = 0; i < size; ++i) { 49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const ChromeSuggestion& suggestion = profile.suggestions(i); 50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) std::string line; 51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) line += "<li><a href=\""; 52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) line += net::EscapeForHTML(suggestion.url()); 53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) line += "\" target=\"_blank\">"; 54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) line += net::EscapeForHTML(suggestion.title()); 55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) std::map<GURL, std::string>::const_iterator it = 56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base64_encoded_pngs.find(GURL(suggestion.url())); 57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (it != base64_encoded_pngs.end()) { 58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) line += "<br><img src='"; 59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) line += it->second; 60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) line += "'>"; 61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) line += "</a></li>\n"; 63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back(line); 64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back("</ul>"); 66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back(kHtmlFooter); 67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *output = JoinString(out, ""); 68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Fills |output| with the HTML needed to display that no suggestions are 71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// available. 72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void RenderOutputHtmlNoSuggestions(std::string* output) { 73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) std::vector<std::string> out; 74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back(kHtmlHeader); 75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back(kHtmlBody); 76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back("<h1>Suggestions</h1>\n"); 77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back("<p>You have no suggestions.</p>\n"); 78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) out.push_back(kHtmlFooter); 79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) *output = JoinString(out, ""); 80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} // namespace 83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)SuggestionsSource::SuggestionsSource(Profile* profile) 85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) : profile_(profile), weak_ptr_factory_(this) {} 86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)SuggestionsSource::~SuggestionsSource() {} 88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)SuggestionsSource::RequestContext::RequestContext( 90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const SuggestionsProfile& suggestions_profile_in, 91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const content::URLDataSource::GotDataCallback& callback_in) 92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) : suggestions_profile(suggestions_profile_in), // Copy. 93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) callback(callback_in) // Copy. 94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){} 95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)SuggestionsSource::RequestContext::~RequestContext() {} 97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)std::string SuggestionsSource::GetSource() const { 99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return chrome::kChromeUISuggestionsHost; 100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void SuggestionsSource::StartDataRequest( 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const std::string& path, int render_process_id, int render_frame_id, 104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const content::URLDataSource::GotDataCallback& callback) { 105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) SuggestionsServiceFactory* suggestions_service_factory = 106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) SuggestionsServiceFactory::GetInstance(); 107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) SuggestionsService* suggestions_service( 109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) suggestions_service_factory->GetForProfile(profile_)); 110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 111010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) if (!suggestions_service) { 112010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) callback.Run(NULL); 113010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) return; 114010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) } 115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) suggestions_service->FetchSuggestionsData( 117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) base::Bind(&SuggestionsSource::OnSuggestionsAvailable, 118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) weak_ptr_factory_.GetWeakPtr(), callback)); 119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)std::string SuggestionsSource::GetMimeType(const std::string& path) const { 122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return "text/html"; 123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)base::MessageLoop* SuggestionsSource::MessageLoopForRequestPath( 126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const std::string& path) const { 127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // This can be accessed from the IO thread. 128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return content::URLDataSource::MessageLoopForRequestPath(path); 129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool SuggestionsSource::ShouldServiceRequest( 132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const net::URLRequest* request) const { 133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (request->url().SchemeIs(chrome::kChromeSearchScheme)) 134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return InstantIOContext::ShouldServiceRequest(request); 135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return URLDataSource::ShouldServiceRequest(request); 136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void SuggestionsSource::OnSuggestionsAvailable( 139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const content::URLDataSource::GotDataCallback& callback, 140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const SuggestionsProfile& suggestions_profile) { 141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) size_t size = suggestions_profile.suggestions_size(); 142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (!size) { 143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) std::string output; 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) RenderOutputHtmlNoSuggestions(&output); 145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) callback.Run(base::RefCountedString::TakeString(&output)); 146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } else { 147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) RequestContext* context = new RequestContext(suggestions_profile, callback); 148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::Closure barrier = BarrierClosure( 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) size, base::Bind(&SuggestionsSource::OnThumbnailsFetched, 150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) weak_ptr_factory_.GetWeakPtr(), context)); 151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for (size_t i = 0; i < size; ++i) { 152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) const ChromeSuggestion& suggestion = suggestions_profile.suggestions(i); 153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // Fetch the thumbnail for this URL (exercising the fetcher). After all 154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // fetches are done, including NULL callbacks for unavailable thumbnails, 155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) // SuggestionsSource::OnThumbnailsFetched will be called. 156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) SuggestionsService* suggestions_service( 157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) SuggestionsServiceFactory::GetForProfile(profile_)); 158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) suggestions_service->GetPageThumbnail( 159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) GURL(suggestion.url()), 160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::Bind(&SuggestionsSource::OnThumbnailAvailable, 161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) weak_ptr_factory_.GetWeakPtr(), context, barrier)); 162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) } 164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void SuggestionsSource::OnThumbnailsFetched(RequestContext* context) { 167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) scoped_ptr<RequestContext> context_deleter(context); 168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) std::string output; 170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) RenderOutputHtml(context->suggestions_profile, context->base64_encoded_pngs, 171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) &output); 172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) context->callback.Run(base::RefCountedString::TakeString(&output)); 173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)} 174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void SuggestionsSource::OnThumbnailAvailable(RequestContext* context, 176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::Closure barrier, 177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const GURL& url, 178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) const SkBitmap* bitmap) { 179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) if (bitmap) { 180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) std::vector<unsigned char> output; 181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) gfx::PNGCodec::EncodeBGRASkBitmap(*bitmap, false, &output); 182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) 183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) std::string encoded_output; 184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) base::Base64Encode(std::string(output.begin(), output.end()), 185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) &encoded_output); 186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) context->base64_encoded_pngs[url] = "data:image/png;base64,"; 187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) context->base64_encoded_pngs[url] += encoded_output; 188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) } 189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) barrier.Run(); 190a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} 191a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 192a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)} // namespace suggestions 193