1 // Copyright (c) 2011 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/extensions/extension_history_api.h"
6
7 #include "base/callback.h"
8 #include "base/json/json_writer.h"
9 #include "base/message_loop.h"
10 #include "base/string_number_conversions.h"
11 #include "base/task.h"
12 #include "base/values.h"
13 #include "chrome/browser/extensions/extension_event_router.h"
14 #include "chrome/browser/extensions/extension_history_api_constants.h"
15 #include "chrome/browser/history/history.h"
16 #include "chrome/browser/history/history_types.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "content/common/notification_service.h"
19 #include "content/common/notification_type.h"
20
21 namespace keys = extension_history_api_constants;
22
23 namespace {
24
MilliSecondsFromTime(const base::Time & time)25 double MilliSecondsFromTime(const base::Time& time) {
26 return 1000 * time.ToDoubleT();
27 }
28
GetHistoryItemDictionary(const history::URLRow & row,DictionaryValue * value)29 void GetHistoryItemDictionary(const history::URLRow& row,
30 DictionaryValue* value) {
31 value->SetString(keys::kIdKey, base::Int64ToString(row.id()));
32 value->SetString(keys::kUrlKey, row.url().spec());
33 value->SetString(keys::kTitleKey, row.title());
34 value->SetDouble(keys::kLastVisitdKey,
35 MilliSecondsFromTime(row.last_visit()));
36 value->SetInteger(keys::kTypedCountKey, row.typed_count());
37 value->SetInteger(keys::kVisitCountKey, row.visit_count());
38 }
39
AddHistoryNode(const history::URLRow & row,ListValue * list)40 void AddHistoryNode(const history::URLRow& row, ListValue* list) {
41 DictionaryValue* dict = new DictionaryValue();
42 GetHistoryItemDictionary(row, dict);
43 list->Append(dict);
44 }
45
GetVisitInfoDictionary(const history::VisitRow & row,DictionaryValue * value)46 void GetVisitInfoDictionary(const history::VisitRow& row,
47 DictionaryValue* value) {
48 value->SetString(keys::kIdKey, base::Int64ToString(row.url_id));
49 value->SetString(keys::kVisitId, base::Int64ToString(row.visit_id));
50 value->SetDouble(keys::kVisitTime, MilliSecondsFromTime(row.visit_time));
51 value->SetString(keys::kReferringVisitId,
52 base::Int64ToString(row.referring_visit));
53
54 const char* trans = PageTransition::CoreTransitionString(row.transition);
55 DCHECK(trans) << "Invalid transition.";
56 value->SetString(keys::kTransition, trans);
57 }
58
AddVisitNode(const history::VisitRow & row,ListValue * list)59 void AddVisitNode(const history::VisitRow& row, ListValue* list) {
60 DictionaryValue* dict = new DictionaryValue();
61 GetVisitInfoDictionary(row, dict);
62 list->Append(dict);
63 }
64
65 } // namespace
66
GetInstance()67 ExtensionHistoryEventRouter* ExtensionHistoryEventRouter::GetInstance() {
68 return Singleton<ExtensionHistoryEventRouter>::get();
69 }
70
ObserveProfile(Profile * profile)71 void ExtensionHistoryEventRouter::ObserveProfile(Profile* profile) {
72 NotificationSource source = Source<Profile>(profile);
73 if (profiles_.find(source.map_key()) == profiles_.end())
74 profiles_[source.map_key()] = profile;
75
76 if (registrar_.IsEmpty()) {
77 registrar_.Add(this,
78 NotificationType::HISTORY_URL_VISITED,
79 NotificationService::AllSources());
80 registrar_.Add(this,
81 NotificationType::HISTORY_URLS_DELETED,
82 NotificationService::AllSources());
83 }
84 }
85
ExtensionHistoryEventRouter()86 ExtensionHistoryEventRouter::ExtensionHistoryEventRouter() {}
87
~ExtensionHistoryEventRouter()88 ExtensionHistoryEventRouter::~ExtensionHistoryEventRouter() {}
89
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)90 void ExtensionHistoryEventRouter::Observe(NotificationType type,
91 const NotificationSource& source,
92 const NotificationDetails& details) {
93 ProfileMap::iterator it = profiles_.find(source.map_key());
94 if (it != profiles_.end()) {
95 Profile* profile = it->second;
96 switch (type.value) {
97 case NotificationType::HISTORY_URL_VISITED:
98 HistoryUrlVisited(
99 profile,
100 Details<const history::URLVisitedDetails>(details).ptr());
101 break;
102 case NotificationType::HISTORY_URLS_DELETED:
103 HistoryUrlsRemoved(
104 profile,
105 Details<const history::URLsDeletedDetails>(details).ptr());
106 break;
107 default:
108 NOTREACHED();
109 }
110 }
111 }
112
HistoryUrlVisited(Profile * profile,const history::URLVisitedDetails * details)113 void ExtensionHistoryEventRouter::HistoryUrlVisited(
114 Profile* profile,
115 const history::URLVisitedDetails* details) {
116 ListValue args;
117 DictionaryValue* dict = new DictionaryValue();
118 GetHistoryItemDictionary(details->row, dict);
119 args.Append(dict);
120
121 std::string json_args;
122 base::JSONWriter::Write(&args, false, &json_args);
123 DispatchEvent(profile, keys::kOnVisited, json_args);
124 }
125
HistoryUrlsRemoved(Profile * profile,const history::URLsDeletedDetails * details)126 void ExtensionHistoryEventRouter::HistoryUrlsRemoved(
127 Profile* profile,
128 const history::URLsDeletedDetails* details) {
129 ListValue args;
130 DictionaryValue* dict = new DictionaryValue();
131 dict->SetBoolean(keys::kAllHistoryKey, details->all_history);
132 ListValue* urls = new ListValue();
133 for (std::set<GURL>::const_iterator iterator = details->urls.begin();
134 iterator != details->urls.end();
135 ++iterator) {
136 urls->Append(new StringValue(iterator->spec()));
137 }
138 dict->Set(keys::kUrlsKey, urls);
139 args.Append(dict);
140
141 std::string json_args;
142 base::JSONWriter::Write(&args, false, &json_args);
143 DispatchEvent(profile, keys::kOnVisitRemoved, json_args);
144 }
145
DispatchEvent(Profile * profile,const char * event_name,const std::string & json_args)146 void ExtensionHistoryEventRouter::DispatchEvent(Profile* profile,
147 const char* event_name,
148 const std::string& json_args) {
149 if (profile && profile->GetExtensionEventRouter()) {
150 profile->GetExtensionEventRouter()->DispatchEventToRenderers(
151 event_name, json_args, profile, GURL());
152 }
153 }
154
Run()155 void HistoryFunction::Run() {
156 if (!RunImpl()) {
157 SendResponse(false);
158 }
159 }
160
GetUrlFromValue(Value * value,GURL * url)161 bool HistoryFunction::GetUrlFromValue(Value* value, GURL* url) {
162 std::string url_string;
163 if (!value->GetAsString(&url_string)) {
164 bad_message_ = true;
165 return false;
166 }
167
168 GURL temp_url(url_string);
169 if (!temp_url.is_valid()) {
170 error_ = keys::kInvalidUrlError;
171 return false;
172 }
173 url->Swap(&temp_url);
174 return true;
175 }
176
GetTimeFromValue(Value * value,base::Time * time)177 bool HistoryFunction::GetTimeFromValue(Value* value, base::Time* time) {
178 double ms_from_epoch = 0.0;
179 if (!value->GetAsDouble(&ms_from_epoch)) {
180 int ms_from_epoch_as_int = 0;
181 if (!value->GetAsInteger(&ms_from_epoch_as_int))
182 return false;
183 ms_from_epoch = static_cast<double>(ms_from_epoch_as_int);
184 }
185 // The history service has seconds resolution, while javascript Date() has
186 // milliseconds resolution.
187 double seconds_from_epoch = ms_from_epoch / 1000.0;
188 // Time::FromDoubleT converts double time 0 to empty Time object. So we need
189 // to do special handling here.
190 *time = (seconds_from_epoch == 0) ?
191 base::Time::UnixEpoch() : base::Time::FromDoubleT(seconds_from_epoch);
192 return true;
193 }
194
HistoryFunctionWithCallback()195 HistoryFunctionWithCallback::HistoryFunctionWithCallback() {
196 }
197
~HistoryFunctionWithCallback()198 HistoryFunctionWithCallback::~HistoryFunctionWithCallback() {
199 }
200
RunImpl()201 bool HistoryFunctionWithCallback::RunImpl() {
202 AddRef(); // Balanced in SendAysncRepose() and below.
203 bool retval = RunAsyncImpl();
204 if (false == retval)
205 Release();
206 return retval;
207 }
208
SendAsyncResponse()209 void HistoryFunctionWithCallback::SendAsyncResponse() {
210 MessageLoop::current()->PostTask(
211 FROM_HERE,
212 NewRunnableMethod(
213 this,
214 &HistoryFunctionWithCallback::SendResponseToCallback));
215 }
216
SendResponseToCallback()217 void HistoryFunctionWithCallback::SendResponseToCallback() {
218 SendResponse(true);
219 Release(); // Balanced in RunImpl().
220 }
221
RunAsyncImpl()222 bool GetVisitsHistoryFunction::RunAsyncImpl() {
223 DictionaryValue* json;
224 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
225
226 Value* value;
227 EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kUrlKey, &value));
228
229 GURL url;
230 if (!GetUrlFromValue(value, &url))
231 return false;
232
233 HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
234 hs->QueryURL(url,
235 true, // Retrieve full history of a URL.
236 &cancelable_consumer_,
237 NewCallback(this, &GetVisitsHistoryFunction::QueryComplete));
238
239 return true;
240 }
241
QueryComplete(HistoryService::Handle request_service,bool success,const history::URLRow * url_row,history::VisitVector * visits)242 void GetVisitsHistoryFunction::QueryComplete(
243 HistoryService::Handle request_service,
244 bool success,
245 const history::URLRow* url_row,
246 history::VisitVector* visits) {
247 ListValue* list = new ListValue();
248 if (visits && !visits->empty()) {
249 for (history::VisitVector::iterator iterator = visits->begin();
250 iterator != visits->end();
251 ++iterator) {
252 AddVisitNode(*iterator, list);
253 }
254 }
255 result_.reset(list);
256 SendAsyncResponse();
257 }
258
RunAsyncImpl()259 bool SearchHistoryFunction::RunAsyncImpl() {
260 DictionaryValue* json;
261 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
262
263 // Initialize the HistoryQuery
264 string16 search_text;
265 EXTENSION_FUNCTION_VALIDATE(json->GetString(keys::kTextKey, &search_text));
266
267 history::QueryOptions options;
268 options.SetRecentDayRange(1);
269 options.max_count = 100;
270
271 if (json->HasKey(keys::kStartTimeKey)) { // Optional.
272 Value* value;
273 EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kStartTimeKey, &value));
274 EXTENSION_FUNCTION_VALIDATE(GetTimeFromValue(value, &options.begin_time));
275 }
276 if (json->HasKey(keys::kEndTimeKey)) { // Optional.
277 Value* value;
278 EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kEndTimeKey, &value));
279 EXTENSION_FUNCTION_VALIDATE(GetTimeFromValue(value, &options.end_time));
280 }
281 if (json->HasKey(keys::kMaxResultsKey)) { // Optional.
282 EXTENSION_FUNCTION_VALIDATE(json->GetInteger(keys::kMaxResultsKey,
283 &options.max_count));
284 }
285
286 HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
287 hs->QueryHistory(search_text, options, &cancelable_consumer_,
288 NewCallback(this, &SearchHistoryFunction::SearchComplete));
289
290 return true;
291 }
292
SearchComplete(HistoryService::Handle request_handle,history::QueryResults * results)293 void SearchHistoryFunction::SearchComplete(
294 HistoryService::Handle request_handle,
295 history::QueryResults* results) {
296 ListValue* list = new ListValue();
297 if (results && !results->empty()) {
298 for (history::QueryResults::URLResultVector::const_iterator iterator =
299 results->begin();
300 iterator != results->end();
301 ++iterator) {
302 AddHistoryNode(**iterator, list);
303 }
304 }
305 result_.reset(list);
306 SendAsyncResponse();
307 }
308
RunImpl()309 bool AddUrlHistoryFunction::RunImpl() {
310 DictionaryValue* json;
311 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
312
313 Value* value;
314 EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kUrlKey, &value));
315
316 GURL url;
317 if (!GetUrlFromValue(value, &url))
318 return false;
319
320 HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
321 hs->AddPage(url, history::SOURCE_EXTENSION);
322
323 SendResponse(true);
324 return true;
325 }
326
RunImpl()327 bool DeleteUrlHistoryFunction::RunImpl() {
328 DictionaryValue* json;
329 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
330
331 Value* value;
332 EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kUrlKey, &value));
333
334 GURL url;
335 if (!GetUrlFromValue(value, &url))
336 return false;
337
338 HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
339 hs->DeleteURL(url);
340
341 SendResponse(true);
342 return true;
343 }
344
RunAsyncImpl()345 bool DeleteRangeHistoryFunction::RunAsyncImpl() {
346 DictionaryValue* json;
347 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
348
349 Value* value = NULL;
350 EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kStartTimeKey, &value));
351 base::Time begin_time;
352 EXTENSION_FUNCTION_VALIDATE(GetTimeFromValue(value, &begin_time));
353
354 EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kEndTimeKey, &value));
355 base::Time end_time;
356 EXTENSION_FUNCTION_VALIDATE(GetTimeFromValue(value, &end_time));
357
358 std::set<GURL> restrict_urls;
359 HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
360 hs->ExpireHistoryBetween(
361 restrict_urls,
362 begin_time,
363 end_time,
364 &cancelable_consumer_,
365 NewCallback(this, &DeleteRangeHistoryFunction::DeleteComplete));
366
367 return true;
368 }
369
DeleteComplete()370 void DeleteRangeHistoryFunction::DeleteComplete() {
371 SendAsyncResponse();
372 }
373
RunAsyncImpl()374 bool DeleteAllHistoryFunction::RunAsyncImpl() {
375 std::set<GURL> restrict_urls;
376 HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
377 hs->ExpireHistoryBetween(
378 restrict_urls,
379 base::Time::UnixEpoch(), // From the beginning of the epoch.
380 base::Time::Now(), // To the current time.
381 &cancelable_consumer_,
382 NewCallback(this, &DeleteAllHistoryFunction::DeleteComplete));
383
384 return true;
385 }
386
DeleteComplete()387 void DeleteAllHistoryFunction::DeleteComplete() {
388 SendAsyncResponse();
389 }
390