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