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 |