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