extension_history_api.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2010 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 "chrome/common/notification_type.h"
19#include "chrome/common/notification_service.h"
20
21namespace keys = extension_history_api_constants;
22
23namespace {
24
25double MilliSecondsFromTime(const base::Time& time) {
26  return 1000 * time.ToDoubleT();
27}
28
29void 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
40void AddHistoryNode(const history::URLRow& row, ListValue* list) {
41  DictionaryValue* dict = new DictionaryValue();
42  GetHistoryItemDictionary(row, dict);
43  list->Append(dict);
44}
45
46void 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
59void 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
67ExtensionHistoryEventRouter* ExtensionHistoryEventRouter::GetInstance() {
68  return Singleton<ExtensionHistoryEventRouter>::get();
69}
70
71void 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
86ExtensionHistoryEventRouter::ExtensionHistoryEventRouter() {}
87
88ExtensionHistoryEventRouter::~ExtensionHistoryEventRouter() {}
89
90void 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
113void 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
126void 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
146void 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
155void HistoryFunction::Run() {
156  if (!RunImpl()) {
157    SendResponse(false);
158  }
159}
160
161bool 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
177bool 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
195HistoryFunctionWithCallback::HistoryFunctionWithCallback() {
196}
197
198HistoryFunctionWithCallback::~HistoryFunctionWithCallback() {
199}
200
201bool HistoryFunctionWithCallback::RunImpl() {
202  AddRef();  // Balanced in SendAysncRepose() and below.
203  bool retval = RunAsyncImpl();
204  if (false == retval)
205    Release();
206  return retval;
207}
208
209void HistoryFunctionWithCallback::SendAsyncResponse() {
210  MessageLoop::current()->PostTask(
211      FROM_HERE,
212      NewRunnableMethod(
213          this,
214          &HistoryFunctionWithCallback::SendResponseToCallback));
215}
216
217void HistoryFunctionWithCallback::SendResponseToCallback() {
218  SendResponse(true);
219  Release();  // Balanced in RunImpl().
220}
221
222bool 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
242void 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
259bool 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
293void 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
309bool 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
327bool 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
345bool 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
370void DeleteRangeHistoryFunction::DeleteComplete() {
371  SendAsyncResponse();
372}
373
374bool 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
387void DeleteAllHistoryFunction::DeleteComplete() {
388  SendAsyncResponse();
389}
390