- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / history / history_api.cc
1 // Copyright (c) 2012 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/api/history/history_api.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/command_line.h"
11 #include "base/json/json_writer.h"
12 #include "base/lazy_instance.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/time/time.h"
19 #include "base/values.h"
20 #include "chrome/browser/chrome_notification_types.h"
21 #include "chrome/browser/extensions/activity_log/activity_log.h"
22 #include "chrome/browser/extensions/event_router.h"
23 #include "chrome/browser/extensions/extension_system.h"
24 #include "chrome/browser/history/history_service.h"
25 #include "chrome/browser/history/history_service_factory.h"
26 #include "chrome/browser/history/history_types.h"
27 #include "chrome/browser/history/visit_filter.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/common/cancelable_task_tracker.h"
30 #include "chrome/common/chrome_switches.h"
31 #include "chrome/common/extensions/api/experimental_history.h"
32 #include "chrome/common/extensions/api/history.h"
33 #include "chrome/common/pref_names.h"
34 #include "content/public/browser/notification_details.h"
35 #include "content/public/browser/notification_source.h"
36
37 namespace extensions {
38
39 using api::experimental_history::MostVisitedItem;
40 using api::history::HistoryItem;
41 using api::history::VisitItem;
42 using extensions::ActivityLog;
43
44 typedef std::vector<linked_ptr<api::history::HistoryItem> >
45     HistoryItemList;
46 typedef std::vector<linked_ptr<api::history::VisitItem> >
47     VisitItemList;
48
49 namespace AddUrl = api::history::AddUrl;
50 namespace DeleteUrl = api::history::DeleteUrl;
51 namespace DeleteRange = api::history::DeleteRange;
52 namespace GetMostVisited = api::experimental_history::GetMostVisited;
53 namespace GetVisits = api::history::GetVisits;
54 namespace OnVisited = api::history::OnVisited;
55 namespace OnVisitRemoved = api::history::OnVisitRemoved;
56 namespace Search = api::history::Search;
57
58 namespace {
59
60 const char kInvalidUrlError[] = "Url is invalid.";
61 const char kDeleteProhibitedError[] = "Browsing history is not allowed to be "
62                                       "deleted.";
63
64 double MilliSecondsFromTime(const base::Time& time) {
65   return 1000 * time.ToDoubleT();
66 }
67
68 scoped_ptr<HistoryItem> GetHistoryItem(const history::URLRow& row) {
69   scoped_ptr<HistoryItem> history_item(new HistoryItem());
70
71   history_item->id = base::Int64ToString(row.id());
72   history_item->url.reset(new std::string(row.url().spec()));
73   history_item->title.reset(new std::string(UTF16ToUTF8(row.title())));
74   history_item->last_visit_time.reset(
75       new double(MilliSecondsFromTime(row.last_visit())));
76   history_item->typed_count.reset(new int(row.typed_count()));
77   history_item->visit_count.reset(new int(row.visit_count()));
78
79   return history_item.Pass();
80 }
81
82 scoped_ptr<VisitItem> GetVisitItem(const history::VisitRow& row) {
83   scoped_ptr<VisitItem> visit_item(new VisitItem());
84
85   visit_item->id = base::Int64ToString(row.url_id);
86   visit_item->visit_id = base::Int64ToString(row.visit_id);
87   visit_item->visit_time.reset(
88       new double(MilliSecondsFromTime(row.visit_time)));
89   visit_item->referring_visit_id = base::Int64ToString(row.referring_visit);
90
91   VisitItem::Transition transition = VisitItem::TRANSITION_LINK;
92   switch (row.transition & content::PAGE_TRANSITION_CORE_MASK) {
93     case content::PAGE_TRANSITION_LINK:
94       transition = VisitItem::TRANSITION_LINK;
95       break;
96     case content::PAGE_TRANSITION_TYPED:
97       transition = VisitItem::TRANSITION_TYPED;
98       break;
99     case content::PAGE_TRANSITION_AUTO_BOOKMARK:
100       transition = VisitItem::TRANSITION_AUTO_BOOKMARK;
101       break;
102     case content::PAGE_TRANSITION_AUTO_SUBFRAME:
103       transition = VisitItem::TRANSITION_AUTO_SUBFRAME;
104       break;
105     case content::PAGE_TRANSITION_MANUAL_SUBFRAME:
106       transition = VisitItem::TRANSITION_MANUAL_SUBFRAME;
107       break;
108     case content::PAGE_TRANSITION_GENERATED:
109       transition = VisitItem::TRANSITION_GENERATED;
110       break;
111     case content::PAGE_TRANSITION_AUTO_TOPLEVEL:
112       transition = VisitItem::TRANSITION_AUTO_TOPLEVEL;
113       break;
114     case content::PAGE_TRANSITION_FORM_SUBMIT:
115       transition = VisitItem::TRANSITION_FORM_SUBMIT;
116       break;
117     case content::PAGE_TRANSITION_RELOAD:
118       transition = VisitItem::TRANSITION_RELOAD;
119       break;
120     case content::PAGE_TRANSITION_KEYWORD:
121       transition = VisitItem::TRANSITION_KEYWORD;
122       break;
123     case content::PAGE_TRANSITION_KEYWORD_GENERATED:
124       transition = VisitItem::TRANSITION_KEYWORD_GENERATED;
125       break;
126     default:
127       DCHECK(false);
128   }
129
130   visit_item->transition = transition;
131
132   return visit_item.Pass();
133 }
134
135 }  // namespace
136
137 HistoryEventRouter::HistoryEventRouter(Profile* profile) {
138   const content::Source<Profile> source = content::Source<Profile>(profile);
139   registrar_.Add(this,
140                  chrome::NOTIFICATION_HISTORY_URL_VISITED,
141                  source);
142   registrar_.Add(this,
143                  chrome::NOTIFICATION_HISTORY_URLS_DELETED,
144                  source);
145 }
146
147 HistoryEventRouter::~HistoryEventRouter() {}
148
149 void HistoryEventRouter::Observe(int type,
150                                  const content::NotificationSource& source,
151                                  const content::NotificationDetails& details) {
152   switch (type) {
153     case chrome::NOTIFICATION_HISTORY_URL_VISITED:
154       HistoryUrlVisited(
155           content::Source<Profile>(source).ptr(),
156           content::Details<const history::URLVisitedDetails>(details).ptr());
157       break;
158     case chrome::NOTIFICATION_HISTORY_URLS_DELETED:
159       HistoryUrlsRemoved(
160           content::Source<Profile>(source).ptr(),
161           content::Details<const history::URLsDeletedDetails>(details).ptr());
162       break;
163     default:
164       NOTREACHED();
165   }
166 }
167
168 void HistoryEventRouter::HistoryUrlVisited(
169     Profile* profile,
170     const history::URLVisitedDetails* details) {
171   scoped_ptr<HistoryItem> history_item = GetHistoryItem(details->row);
172   scoped_ptr<base::ListValue> args = OnVisited::Create(*history_item);
173
174   DispatchEvent(profile, api::history::OnVisited::kEventName, args.Pass());
175 }
176
177 void HistoryEventRouter::HistoryUrlsRemoved(
178     Profile* profile,
179     const history::URLsDeletedDetails* details) {
180   OnVisitRemoved::Removed removed;
181   removed.all_history = details->all_history;
182
183   std::vector<std::string>* urls = new std::vector<std::string>();
184   for (history::URLRows::const_iterator iterator = details->rows.begin();
185       iterator != details->rows.end(); ++iterator) {
186     urls->push_back(iterator->url().spec());
187   }
188   removed.urls.reset(urls);
189
190   scoped_ptr<base::ListValue> args = OnVisitRemoved::Create(removed);
191   DispatchEvent(profile, api::history::OnVisitRemoved::kEventName, args.Pass());
192 }
193
194 void HistoryEventRouter::DispatchEvent(
195     Profile* profile,
196     const std::string& event_name,
197     scoped_ptr<base::ListValue> event_args) {
198   if (profile && extensions::ExtensionSystem::Get(profile)->event_router()) {
199     scoped_ptr<extensions::Event> event(new extensions::Event(
200         event_name, event_args.Pass()));
201     event->restrict_to_profile = profile;
202     extensions::ExtensionSystem::Get(profile)->event_router()->
203         BroadcastEvent(event.Pass());
204   }
205 }
206
207 HistoryAPI::HistoryAPI(Profile* profile) : profile_(profile) {
208   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
209       this, api::history::OnVisited::kEventName);
210   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
211       this, api::history::OnVisitRemoved::kEventName);
212 }
213
214 HistoryAPI::~HistoryAPI() {
215 }
216
217 void HistoryAPI::Shutdown() {
218   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
219 }
220
221 static base::LazyInstance<ProfileKeyedAPIFactory<HistoryAPI> >
222 g_factory = LAZY_INSTANCE_INITIALIZER;
223
224 // static
225 ProfileKeyedAPIFactory<HistoryAPI>* HistoryAPI::GetFactoryInstance() {
226   return &g_factory.Get();
227 }
228
229 template<>
230 void ProfileKeyedAPIFactory<HistoryAPI>::DeclareFactoryDependencies() {
231   DependsOn(ActivityLogFactory::GetInstance());
232 }
233
234 void HistoryAPI::OnListenerAdded(const EventListenerInfo& details) {
235   history_event_router_.reset(new HistoryEventRouter(profile_));
236   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
237 }
238
239 void HistoryFunction::Run() {
240   if (!RunImpl()) {
241     SendResponse(false);
242   }
243 }
244
245 bool HistoryFunction::ValidateUrl(const std::string& url_string, GURL* url) {
246   GURL temp_url(url_string);
247   if (!temp_url.is_valid()) {
248     error_ = kInvalidUrlError;
249     return false;
250   }
251   url->Swap(&temp_url);
252   return true;
253 }
254
255 bool HistoryFunction::VerifyDeleteAllowed() {
256   PrefService* prefs = GetProfile()->GetPrefs();
257   if (!prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory)) {
258     error_ = kDeleteProhibitedError;
259     return false;
260   }
261   return true;
262 }
263
264 base::Time HistoryFunction::GetTime(double ms_from_epoch) {
265   // The history service has seconds resolution, while javascript Date() has
266   // milliseconds resolution.
267   double seconds_from_epoch = ms_from_epoch / 1000.0;
268   // Time::FromDoubleT converts double time 0 to empty Time object. So we need
269   // to do special handling here.
270   return (seconds_from_epoch == 0) ?
271       base::Time::UnixEpoch() : base::Time::FromDoubleT(seconds_from_epoch);
272 }
273
274 HistoryFunctionWithCallback::HistoryFunctionWithCallback() {
275 }
276
277 HistoryFunctionWithCallback::~HistoryFunctionWithCallback() {
278 }
279
280 bool HistoryFunctionWithCallback::RunImpl() {
281   AddRef();  // Balanced in SendAysncRepose() and below.
282   bool retval = RunAsyncImpl();
283   if (false == retval)
284     Release();
285   return retval;
286 }
287
288 void HistoryFunctionWithCallback::SendAsyncResponse() {
289   base::MessageLoop::current()->PostTask(
290       FROM_HERE,
291       base::Bind(&HistoryFunctionWithCallback::SendResponseToCallback, this));
292 }
293
294 void HistoryFunctionWithCallback::SendResponseToCallback() {
295   SendResponse(true);
296   Release();  // Balanced in RunImpl().
297 }
298
299 bool HistoryGetMostVisitedFunction::RunAsyncImpl() {
300   scoped_ptr<GetMostVisited::Params> params =
301       GetMostVisited::Params::Create(*args_);
302   EXTENSION_FUNCTION_VALIDATE(params.get());
303
304   history::VisitFilter filter;
305   if (params->details.filter_time.get())
306     filter.SetFilterTime(GetTime(*params->details.filter_time));
307   if (params->details.filter_width.get()) {
308     filter.SetFilterWidth(base::TimeDelta::FromMilliseconds(
309         static_cast<int64>(*params->details.filter_width)));
310   }
311   if (params->details.day_of_the_week.get())
312     filter.SetDayOfTheWeekFilter(*params->details.day_of_the_week);
313   int max_results = 100;
314   if (params->details.max_results.get())
315     max_results = *params->details.max_results;
316   HistoryService* hs = HistoryServiceFactory::GetForProfile(
317       GetProfile(), Profile::EXPLICIT_ACCESS);
318   hs->QueryFilteredURLs(max_results, filter, false, &cancelable_consumer_,
319       base::Bind(&HistoryGetMostVisitedFunction::QueryComplete,
320                  base::Unretained(this)));
321   return true;
322 }
323
324 void HistoryGetMostVisitedFunction::QueryComplete(
325     CancelableRequestProvider::Handle handle,
326     const history::FilteredURLList& data) {
327   std::vector<linked_ptr<MostVisitedItem> > results;
328   results.reserve(data.size());
329   for (size_t i = 0; i < data.size(); i++) {
330     linked_ptr<MostVisitedItem> item(new MostVisitedItem);
331     item->url = data[i].url.spec();
332     item->title = UTF16ToUTF8(data[i].title);
333     results.push_back(item);
334   }
335   results_ = GetMostVisited::Results::Create(results);
336   SendAsyncResponse();
337 }
338
339 bool HistoryGetVisitsFunction::RunAsyncImpl() {
340   scoped_ptr<GetVisits::Params> params(GetVisits::Params::Create(*args_));
341   EXTENSION_FUNCTION_VALIDATE(params.get());
342
343   GURL url;
344   if (!ValidateUrl(params->details.url, &url))
345     return false;
346
347   HistoryService* hs = HistoryServiceFactory::GetForProfile(
348       GetProfile(), Profile::EXPLICIT_ACCESS);
349   hs->QueryURL(url,
350                true,  // Retrieve full history of a URL.
351                &cancelable_consumer_,
352                base::Bind(&HistoryGetVisitsFunction::QueryComplete,
353                           base::Unretained(this)));
354
355   return true;
356 }
357
358 void HistoryGetVisitsFunction::QueryComplete(
359     HistoryService::Handle request_service,
360     bool success,
361     const history::URLRow* url_row,
362     history::VisitVector* visits) {
363   VisitItemList visit_item_vec;
364   if (visits && !visits->empty()) {
365     for (history::VisitVector::iterator iterator = visits->begin();
366          iterator != visits->end();
367          ++iterator) {
368       visit_item_vec.push_back(make_linked_ptr(
369           GetVisitItem(*iterator).release()));
370     }
371   }
372
373   results_ = GetVisits::Results::Create(visit_item_vec);
374   SendAsyncResponse();
375 }
376
377 bool HistorySearchFunction::RunAsyncImpl() {
378   scoped_ptr<Search::Params> params(Search::Params::Create(*args_));
379   EXTENSION_FUNCTION_VALIDATE(params.get());
380
381   string16 search_text = UTF8ToUTF16(params->query.text);
382
383   history::QueryOptions options;
384   options.SetRecentDayRange(1);
385   options.max_count = 100;
386
387   if (params->query.start_time.get())
388     options.begin_time = GetTime(*params->query.start_time);
389   if (params->query.end_time.get())
390     options.end_time = GetTime(*params->query.end_time);
391   if (params->query.max_results.get())
392     options.max_count = *params->query.max_results;
393
394   HistoryService* hs = HistoryServiceFactory::GetForProfile(
395       GetProfile(), Profile::EXPLICIT_ACCESS);
396   hs->QueryHistory(search_text, options, &cancelable_consumer_,
397                    base::Bind(&HistorySearchFunction::SearchComplete,
398                               base::Unretained(this)));
399
400   return true;
401 }
402
403 void HistorySearchFunction::SearchComplete(
404     HistoryService::Handle request_handle,
405     history::QueryResults* results) {
406   HistoryItemList history_item_vec;
407   if (results && !results->empty()) {
408     for (history::QueryResults::URLResultVector::const_iterator iterator =
409             results->begin();
410          iterator != results->end();
411         ++iterator) {
412       history_item_vec.push_back(make_linked_ptr(
413           GetHistoryItem(**iterator).release()));
414     }
415   }
416   results_ = Search::Results::Create(history_item_vec);
417   SendAsyncResponse();
418 }
419
420 bool HistoryAddUrlFunction::RunImpl() {
421   scoped_ptr<AddUrl::Params> params(AddUrl::Params::Create(*args_));
422   EXTENSION_FUNCTION_VALIDATE(params.get());
423
424   GURL url;
425   if (!ValidateUrl(params->details.url, &url))
426     return false;
427
428   HistoryService* hs = HistoryServiceFactory::GetForProfile(
429       GetProfile(), Profile::EXPLICIT_ACCESS);
430   hs->AddPage(url, base::Time::Now(), history::SOURCE_EXTENSION);
431
432   SendResponse(true);
433   return true;
434 }
435
436 bool HistoryDeleteUrlFunction::RunImpl() {
437   scoped_ptr<DeleteUrl::Params> params(DeleteUrl::Params::Create(*args_));
438   EXTENSION_FUNCTION_VALIDATE(params.get());
439
440   if (!VerifyDeleteAllowed())
441     return false;
442
443   GURL url;
444   if (!ValidateUrl(params->details.url, &url))
445     return false;
446
447   HistoryService* hs = HistoryServiceFactory::GetForProfile(
448       GetProfile(), Profile::EXPLICIT_ACCESS);
449   hs->DeleteURL(url);
450
451   // Also clean out from the activity log. If the activity log testing flag is
452   // set then don't clean so testers can see what potentially malicious
453   // extensions have been trying to clean from their logs.
454   if (!CommandLine::ForCurrentProcess()->HasSwitch(
455           switches::kEnableExtensionActivityLogTesting)) {
456     ActivityLog* activity_log = ActivityLog::GetInstance(GetProfile());
457     DCHECK(activity_log);
458     activity_log->RemoveURL(url);
459   }
460
461   SendResponse(true);
462   return true;
463 }
464
465 bool HistoryDeleteRangeFunction::RunAsyncImpl() {
466   scoped_ptr<DeleteRange::Params> params(DeleteRange::Params::Create(*args_));
467   EXTENSION_FUNCTION_VALIDATE(params.get());
468
469   if (!VerifyDeleteAllowed())
470     return false;
471
472   base::Time start_time = GetTime(params->range.start_time);
473   base::Time end_time = GetTime(params->range.end_time);
474
475   std::set<GURL> restrict_urls;
476   HistoryService* hs = HistoryServiceFactory::GetForProfile(
477       GetProfile(), Profile::EXPLICIT_ACCESS);
478   hs->ExpireHistoryBetween(
479       restrict_urls,
480       start_time,
481       end_time,
482       base::Bind(&HistoryDeleteRangeFunction::DeleteComplete,
483                  base::Unretained(this)),
484       &task_tracker_);
485
486   // Also clean from the activity log unless in testing mode.
487   if (!CommandLine::ForCurrentProcess()->HasSwitch(
488           switches::kEnableExtensionActivityLogTesting)) {
489     ActivityLog* activity_log = ActivityLog::GetInstance(GetProfile());
490     DCHECK(activity_log);
491     activity_log->RemoveURLs(restrict_urls);
492   }
493
494   return true;
495 }
496
497 void HistoryDeleteRangeFunction::DeleteComplete() {
498   SendAsyncResponse();
499 }
500
501 bool HistoryDeleteAllFunction::RunAsyncImpl() {
502   if (!VerifyDeleteAllowed())
503     return false;
504
505   std::set<GURL> restrict_urls;
506   HistoryService* hs = HistoryServiceFactory::GetForProfile(
507       GetProfile(), Profile::EXPLICIT_ACCESS);
508   hs->ExpireHistoryBetween(
509       restrict_urls,
510       base::Time(),      // Unbounded beginning...
511       base::Time(),      // ...and the end.
512       base::Bind(&HistoryDeleteAllFunction::DeleteComplete,
513                  base::Unretained(this)),
514       &task_tracker_);
515
516   // Also clean from the activity log unless in testing mode.
517   if (!CommandLine::ForCurrentProcess()->HasSwitch(
518           switches::kEnableExtensionActivityLogTesting)) {
519     ActivityLog* activity_log = ActivityLog::GetInstance(GetProfile());
520     DCHECK(activity_log);
521     activity_log->RemoveURLs(restrict_urls);
522   }
523
524   return true;
525 }
526
527 void HistoryDeleteAllFunction::DeleteComplete() {
528   SendAsyncResponse();
529 }
530
531 }  // namespace extensions