| OLD | NEW |
| (Empty) |
| 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 #include "chrome/browser/ui/webui/ntp/suggestions_combiner.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/values.h" | |
| 10 #include "chrome/browser/profiles/profile.h" | |
| 11 #include "chrome/browser/ui/browser.h" | |
| 12 #include "chrome/browser/ui/browser_iterator.h" | |
| 13 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 14 #include "chrome/browser/ui/webui/ntp/suggestions_page_handler.h" | |
| 15 #include "chrome/browser/ui/webui/ntp/suggestions_source.h" | |
| 16 #include "content/public/browser/web_contents.h" | |
| 17 | |
| 18 namespace { | |
| 19 | |
| 20 static const size_t kSuggestionsCount = 8; | |
| 21 | |
| 22 } // namespace | |
| 23 | |
| 24 SuggestionsCombiner::SuggestionsCombiner( | |
| 25 SuggestionsCombiner::Delegate* delegate, | |
| 26 Profile* profile) | |
| 27 : sources_fetching_count_(0), | |
| 28 delegate_(delegate), | |
| 29 suggestions_count_(kSuggestionsCount), | |
| 30 page_values_(new base::ListValue()), | |
| 31 debug_enabled_(false), | |
| 32 profile_(profile) { | |
| 33 } | |
| 34 | |
| 35 SuggestionsCombiner::~SuggestionsCombiner() { | |
| 36 } | |
| 37 | |
| 38 void SuggestionsCombiner::AddSource(SuggestionsSource* source) { | |
| 39 source->SetCombiner(this); | |
| 40 source->SetDebug(debug_enabled_); | |
| 41 sources_.push_back(source); | |
| 42 } | |
| 43 | |
| 44 void SuggestionsCombiner::EnableDebug(bool enable) { | |
| 45 debug_enabled_ = enable; | |
| 46 for (size_t i = 0; i < sources_.size(); ++i) { | |
| 47 sources_[i]->SetDebug(enable); | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 void SuggestionsCombiner::FetchItems(Profile* profile) { | |
| 52 sources_fetching_count_ = sources_.size(); | |
| 53 for (size_t i = 0; i < sources_.size(); ++i) { | |
| 54 sources_[i]->FetchItems(profile); | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 base::ListValue* SuggestionsCombiner::GetPageValues() { | |
| 59 return page_values_.get(); | |
| 60 } | |
| 61 | |
| 62 void SuggestionsCombiner::OnItemsReady() { | |
| 63 DCHECK_GT(sources_fetching_count_, 0); | |
| 64 sources_fetching_count_--; | |
| 65 if (sources_fetching_count_ == 0) { | |
| 66 FillPageValues(); | |
| 67 delegate_->OnSuggestionsReady(); | |
| 68 } | |
| 69 } | |
| 70 | |
| 71 void SuggestionsCombiner::SetSuggestionsCount(size_t suggestions_count) { | |
| 72 suggestions_count_ = suggestions_count; | |
| 73 } | |
| 74 | |
| 75 void SuggestionsCombiner::FillPageValues() { | |
| 76 int total_weight = 0; | |
| 77 for (size_t i = 0; i < sources_.size(); ++i) | |
| 78 total_weight += sources_[i]->GetWeight(); | |
| 79 DCHECK_GT(total_weight, 0); | |
| 80 | |
| 81 page_values_.reset(new base::ListValue()); | |
| 82 | |
| 83 // Evaluate how many items to obtain from each source. We use error diffusion | |
| 84 // to ensure that we get the total desired number of items. | |
| 85 int error = 0; | |
| 86 | |
| 87 // Holds the index at which the next item should be added for each source. | |
| 88 std::vector<size_t> next_item_index_for_source; | |
| 89 next_item_index_for_source.reserve(sources_.size()); | |
| 90 for (size_t i = 0; i < sources_.size(); ++i) { | |
| 91 int numerator = sources_[i]->GetWeight() * suggestions_count_ + error; | |
| 92 error = numerator % total_weight; | |
| 93 int item_count = std::min(numerator / total_weight, | |
| 94 sources_[i]->GetItemCount()); | |
| 95 | |
| 96 for (int j = 0; j < item_count; ++j) | |
| 97 page_values_->Append(sources_[i]->PopItem()); | |
| 98 next_item_index_for_source.push_back(page_values_->GetSize()); | |
| 99 } | |
| 100 | |
| 101 // Fill in extra items, prioritizing the first source. | |
| 102 // Rather than updating |next_item_index_for_source| we keep track of the | |
| 103 // number of extra items that were added and offset indices by that much. | |
| 104 size_t extra_items_added = 0; | |
| 105 for (size_t i = 0; i < sources_.size() && | |
| 106 page_values_->GetSize() < suggestions_count_; ++i) { | |
| 107 | |
| 108 size_t index = next_item_index_for_source[i] + extra_items_added; | |
| 109 while (page_values_->GetSize() < suggestions_count_) { | |
| 110 base::DictionaryValue* item = sources_[i]->PopItem(); | |
| 111 if (!item) | |
| 112 break; | |
| 113 page_values_->Insert(index++, item); | |
| 114 extra_items_added++; | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 // Add page value information common to all sources. | |
| 119 for (size_t i = 0; i < page_values_->GetSize(); i++) { | |
| 120 base::DictionaryValue* page_value; | |
| 121 if (page_values_->GetDictionary(i, &page_value)) | |
| 122 AddExtendedInformation(page_value); | |
| 123 } | |
| 124 } | |
| 125 | |
| 126 void SuggestionsCombiner::AddExtendedInformation( | |
| 127 base::DictionaryValue* page_value) { | |
| 128 if (debug_enabled_) { | |
| 129 std::string url_string; | |
| 130 if (page_value->GetString("url", &url_string)) { | |
| 131 GURL url(url_string); | |
| 132 page_value->SetBoolean("already_open", IsUrlAlreadyOpen(url)); | |
| 133 } | |
| 134 } | |
| 135 } | |
| 136 | |
| 137 bool SuggestionsCombiner::IsUrlAlreadyOpen(const GURL &url) { | |
| 138 for (chrome::BrowserIterator it; !it.done(); it.Next()) { | |
| 139 const Browser* browser = *it; | |
| 140 if (browser->profile()->IsOffTheRecord() || | |
| 141 !browser->profile()->IsSameProfile(profile_)) | |
| 142 continue; | |
| 143 | |
| 144 for (int i = 0; i < browser->tab_strip_model()->count(); i++) { | |
| 145 const content::WebContents* tab = | |
| 146 browser->tab_strip_model()->GetWebContentsAt(i); | |
| 147 if (tab->GetURL() == url) | |
| 148 return true; | |
| 149 } | |
| 150 } | |
| 151 return false; | |
| 152 } | |
| OLD | NEW |