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