• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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