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