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.
5 #include "chrome/browser/extensions/api/history/history_api.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"
35 namespace extensions {
37 using api::history::HistoryItem;
38 using api::history::VisitItem;
39 using extensions::ActivityLog;
41 typedef std::vector<linked_ptr<api::history::HistoryItem> >
43 typedef std::vector<linked_ptr<api::history::VisitItem> >
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;
56 const char kInvalidUrlError[] = "Url is invalid.";
57 const char kDeleteProhibitedError[] = "Browsing history is not allowed to be "
60 double MilliSecondsFromTime(const base::Time& time) {
61 return 1000 * time.ToDoubleT();
64 scoped_ptr<HistoryItem> GetHistoryItem(const history::URLRow& row) {
65 scoped_ptr<HistoryItem> history_item(new HistoryItem());
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()));
75 return history_item.Pass();
78 scoped_ptr<VisitItem> GetVisitItem(const history::VisitRow& row) {
79 scoped_ptr<VisitItem> visit_item(new VisitItem());
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);
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;
92 case content::PAGE_TRANSITION_TYPED:
93 transition = VisitItem::TRANSITION_TYPED;
95 case content::PAGE_TRANSITION_AUTO_BOOKMARK:
96 transition = VisitItem::TRANSITION_AUTO_BOOKMARK;
98 case content::PAGE_TRANSITION_AUTO_SUBFRAME:
99 transition = VisitItem::TRANSITION_AUTO_SUBFRAME;
101 case content::PAGE_TRANSITION_MANUAL_SUBFRAME:
102 transition = VisitItem::TRANSITION_MANUAL_SUBFRAME;
104 case content::PAGE_TRANSITION_GENERATED:
105 transition = VisitItem::TRANSITION_GENERATED;
107 case content::PAGE_TRANSITION_AUTO_TOPLEVEL:
108 transition = VisitItem::TRANSITION_AUTO_TOPLEVEL;
110 case content::PAGE_TRANSITION_FORM_SUBMIT:
111 transition = VisitItem::TRANSITION_FORM_SUBMIT;
113 case content::PAGE_TRANSITION_RELOAD:
114 transition = VisitItem::TRANSITION_RELOAD;
116 case content::PAGE_TRANSITION_KEYWORD:
117 transition = VisitItem::TRANSITION_KEYWORD;
119 case content::PAGE_TRANSITION_KEYWORD_GENERATED:
120 transition = VisitItem::TRANSITION_KEYWORD_GENERATED;
126 visit_item->transition = transition;
128 return visit_item.Pass();
133 HistoryEventRouter::HistoryEventRouter(Profile* profile) {
134 const content::Source<Profile> source = content::Source<Profile>(profile);
136 chrome::NOTIFICATION_HISTORY_URL_VISITED,
139 chrome::NOTIFICATION_HISTORY_URLS_DELETED,
143 HistoryEventRouter::~HistoryEventRouter() {}
145 void HistoryEventRouter::Observe(int type,
146 const content::NotificationSource& source,
147 const content::NotificationDetails& details) {
149 case chrome::NOTIFICATION_HISTORY_URL_VISITED:
151 content::Source<Profile>(source).ptr(),
152 content::Details<const history::URLVisitedDetails>(details).ptr());
154 case chrome::NOTIFICATION_HISTORY_URLS_DELETED:
156 content::Source<Profile>(source).ptr(),
157 content::Details<const history::URLsDeletedDetails>(details).ptr());
164 void HistoryEventRouter::HistoryUrlVisited(
166 const history::URLVisitedDetails* details) {
167 scoped_ptr<HistoryItem> history_item = GetHistoryItem(details->row);
168 scoped_ptr<base::ListValue> args = OnVisited::Create(*history_item);
170 DispatchEvent(profile, api::history::OnVisited::kEventName, args.Pass());
173 void HistoryEventRouter::HistoryUrlsRemoved(
175 const history::URLsDeletedDetails* details) {
176 OnVisitRemoved::Removed removed;
177 removed.all_history = details->all_history;
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());
184 removed.urls.reset(urls);
186 scoped_ptr<base::ListValue> args = OnVisitRemoved::Create(removed);
187 DispatchEvent(profile, api::history::OnVisitRemoved::kEventName, args.Pass());
190 void HistoryEventRouter::DispatchEvent(
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());
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);
210 HistoryAPI::~HistoryAPI() {
213 void HistoryAPI::Shutdown() {
214 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
217 static base::LazyInstance<ProfileKeyedAPIFactory<HistoryAPI> >
218 g_factory = LAZY_INSTANCE_INITIALIZER;
221 ProfileKeyedAPIFactory<HistoryAPI>* HistoryAPI::GetFactoryInstance() {
222 return g_factory.Pointer();
226 void ProfileKeyedAPIFactory<HistoryAPI>::DeclareFactoryDependencies() {
227 DependsOn(ActivityLogFactory::GetInstance());
230 void HistoryAPI::OnListenerAdded(const EventListenerInfo& details) {
231 history_event_router_.reset(new HistoryEventRouter(profile_));
232 ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
235 void HistoryFunction::Run() {
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;
247 url->Swap(&temp_url);
251 bool HistoryFunction::VerifyDeleteAllowed() {
252 PrefService* prefs = GetProfile()->GetPrefs();
253 if (!prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory)) {
254 error_ = kDeleteProhibitedError;
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);
270 HistoryFunctionWithCallback::HistoryFunctionWithCallback() {
273 HistoryFunctionWithCallback::~HistoryFunctionWithCallback() {
276 bool HistoryFunctionWithCallback::RunImpl() {
277 AddRef(); // Balanced in SendAysncRepose() and below.
278 bool retval = RunAsyncImpl();
284 void HistoryFunctionWithCallback::SendAsyncResponse() {
285 base::MessageLoop::current()->PostTask(
287 base::Bind(&HistoryFunctionWithCallback::SendResponseToCallback, this));
290 void HistoryFunctionWithCallback::SendResponseToCallback() {
292 Release(); // Balanced in RunImpl().
295 bool HistoryGetVisitsFunction::RunAsyncImpl() {
296 scoped_ptr<GetVisits::Params> params(GetVisits::Params::Create(*args_));
297 EXTENSION_FUNCTION_VALIDATE(params.get());
300 if (!ValidateUrl(params->details.url, &url))
303 HistoryService* hs = HistoryServiceFactory::GetForProfile(
304 GetProfile(), Profile::EXPLICIT_ACCESS);
306 true, // Retrieve full history of a URL.
307 &cancelable_consumer_,
308 base::Bind(&HistoryGetVisitsFunction::QueryComplete,
309 base::Unretained(this)));
314 void HistoryGetVisitsFunction::QueryComplete(
315 HistoryService::Handle request_service,
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();
324 visit_item_vec.push_back(make_linked_ptr(
325 GetVisitItem(*iterator).release()));
329 results_ = GetVisits::Results::Create(visit_item_vec);
333 bool HistorySearchFunction::RunAsyncImpl() {
334 scoped_ptr<Search::Params> params(Search::Params::Create(*args_));
335 EXTENSION_FUNCTION_VALIDATE(params.get());
337 base::string16 search_text = base::UTF8ToUTF16(params->query.text);
339 history::QueryOptions options;
340 options.SetRecentDayRange(1);
341 options.max_count = 100;
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;
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)));
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 =
366 iterator != results->end();
368 history_item_vec.push_back(make_linked_ptr(
369 GetHistoryItem(**iterator).release()));
372 results_ = Search::Results::Create(history_item_vec);
376 bool HistoryAddUrlFunction::RunImpl() {
377 scoped_ptr<AddUrl::Params> params(AddUrl::Params::Create(*args_));
378 EXTENSION_FUNCTION_VALIDATE(params.get());
381 if (!ValidateUrl(params->details.url, &url))
384 HistoryService* hs = HistoryServiceFactory::GetForProfile(
385 GetProfile(), Profile::EXPLICIT_ACCESS);
386 hs->AddPage(url, base::Time::Now(), history::SOURCE_EXTENSION);
392 bool HistoryDeleteUrlFunction::RunImpl() {
393 scoped_ptr<DeleteUrl::Params> params(DeleteUrl::Params::Create(*args_));
394 EXTENSION_FUNCTION_VALIDATE(params.get());
396 if (!VerifyDeleteAllowed())
400 if (!ValidateUrl(params->details.url, &url))
403 HistoryService* hs = HistoryServiceFactory::GetForProfile(
404 GetProfile(), Profile::EXPLICIT_ACCESS);
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);
421 bool HistoryDeleteRangeFunction::RunAsyncImpl() {
422 scoped_ptr<DeleteRange::Params> params(DeleteRange::Params::Create(*args_));
423 EXTENSION_FUNCTION_VALIDATE(params.get());
425 if (!VerifyDeleteAllowed())
428 base::Time start_time = GetTime(params->range.start_time);
429 base::Time end_time = GetTime(params->range.end_time);
431 std::set<GURL> restrict_urls;
432 HistoryService* hs = HistoryServiceFactory::GetForProfile(
433 GetProfile(), Profile::EXPLICIT_ACCESS);
434 hs->ExpireHistoryBetween(
438 base::Bind(&HistoryDeleteRangeFunction::DeleteComplete,
439 base::Unretained(this)),
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);
453 void HistoryDeleteRangeFunction::DeleteComplete() {
457 bool HistoryDeleteAllFunction::RunAsyncImpl() {
458 if (!VerifyDeleteAllowed())
461 std::set<GURL> restrict_urls;
462 HistoryService* hs = HistoryServiceFactory::GetForProfile(
463 GetProfile(), Profile::EXPLICIT_ACCESS);
464 hs->ExpireHistoryBetween(
466 base::Time(), // Unbounded beginning...
467 base::Time(), // ...and the end.
468 base::Bind(&HistoryDeleteAllFunction::DeleteComplete,
469 base::Unretained(this)),
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);
483 void HistoryDeleteAllFunction::DeleteComplete() {
487 } // namespace extensions