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.
5 #include "chrome/browser/ui/webui/ntp/suggestions_combiner.h"
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"
20 static const size_t kSuggestionsCount
= 8;
24 SuggestionsCombiner::SuggestionsCombiner(
25 SuggestionsCombiner::Delegate
* delegate
,
27 : sources_fetching_count_(0),
29 suggestions_count_(kSuggestionsCount
),
30 page_values_(new base::ListValue()),
31 debug_enabled_(false),
35 SuggestionsCombiner::~SuggestionsCombiner() {
38 void SuggestionsCombiner::AddSource(SuggestionsSource
* source
) {
39 source
->SetCombiner(this);
40 source
->SetDebug(debug_enabled_
);
41 sources_
.push_back(source
);
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
);
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
);
58 base::ListValue
* SuggestionsCombiner::GetPageValues() {
59 return page_values_
.get();
62 void SuggestionsCombiner::OnItemsReady() {
63 DCHECK_GT(sources_fetching_count_
, 0);
64 sources_fetching_count_
--;
65 if (sources_fetching_count_
== 0) {
67 delegate_
->OnSuggestionsReady();
71 void SuggestionsCombiner::SetSuggestionsCount(size_t suggestions_count
) {
72 suggestions_count_
= suggestions_count
;
75 void SuggestionsCombiner::FillPageValues() {
77 for (size_t i
= 0; i
< sources_
.size(); ++i
)
78 total_weight
+= sources_
[i
]->GetWeight();
79 DCHECK_GT(total_weight
, 0);
81 page_values_
.reset(new base::ListValue());
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.
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());
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());
101 // Fill in extra items, prioritizing the first source.
102 base::DictionaryValue
* item
;
103 // Rather than updating |next_item_index_for_source| we keep track of the
104 // number of extra items that were added and offset indices by that much.
105 size_t extra_items_added
= 0;
106 for (size_t i
= 0; i
< sources_
.size() &&
107 page_values_
->GetSize() < suggestions_count_
; ++i
) {
109 size_t index
= next_item_index_for_source
[i
] + extra_items_added
;
110 while (page_values_
->GetSize() < suggestions_count_
&&
111 (item
= sources_
[i
]->PopItem())) {
112 page_values_
->Insert(index
++, item
);
117 // Add page value information common to all sources.
118 for (size_t i
= 0; i
< page_values_
->GetSize(); i
++) {
119 base::DictionaryValue
* page_value
;
120 if (page_values_
->GetDictionary(i
, &page_value
))
121 AddExtendedInformation(page_value
);
125 void SuggestionsCombiner::AddExtendedInformation(
126 base::DictionaryValue
* page_value
) {
127 if (debug_enabled_
) {
128 std::string url_string
;
129 if (page_value
->GetString("url", &url_string
)) {
130 GURL
url(url_string
);
131 page_value
->SetBoolean("already_open", IsUrlAlreadyOpen(url
));
136 bool SuggestionsCombiner::IsUrlAlreadyOpen(const GURL
&url
) {
137 for (chrome::BrowserIterator it
; !it
.done(); it
.Next()) {
138 const Browser
* browser
= *it
;
139 if (browser
->profile()->IsOffTheRecord() ||
140 !browser
->profile()->IsSameProfile(profile_
))
143 for (int i
= 0; i
< browser
->tab_strip_model()->count(); i
++) {
144 const content::WebContents
* tab
=
145 browser
->tab_strip_model()->GetWebContentsAt(i
);
146 if (tab
->GetURL() == url
)