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