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
MilliSecondsFromTime(const base::Time & time)61 double MilliSecondsFromTime(const base::Time& time) {
62 return 1000 * time.ToDoubleT();
63 }
64
GetHistoryItem(const history::URLRow & row)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
GetVisitItem(const history::VisitRow & row)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
HistoryEventRouter(Profile * profile)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
~HistoryEventRouter()144 HistoryEventRouter::~HistoryEventRouter() {}
145
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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
HistoryUrlVisited(Profile * profile,const history::URLVisitedDetails * details)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
HistoryUrlsRemoved(Profile * profile,const history::URLsDeletedDetails * details)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
DispatchEvent(Profile * profile,const std::string & event_name,scoped_ptr<base::ListValue> event_args)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
HistoryAPI(content::BrowserContext * context)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
~HistoryAPI()211 HistoryAPI::~HistoryAPI() {
212 }
213
Shutdown()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
GetFactoryInstance()222 BrowserContextKeyedAPIFactory<HistoryAPI>* HistoryAPI::GetFactoryInstance() {
223 return g_factory.Pointer();
224 }
225
226 template <>
DeclareFactoryDependencies()227 void BrowserContextKeyedAPIFactory<HistoryAPI>::DeclareFactoryDependencies() {
228 DependsOn(ActivityLog::GetFactoryInstance());
229 DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory());
230 }
231
OnListenerAdded(const EventListenerInfo & details)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
ValidateUrl(const std::string & url_string,GURL * url)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
VerifyDeleteAllowed()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
GetTime(double ms_from_epoch)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
HistoryFunctionWithCallback()267 HistoryFunctionWithCallback::HistoryFunctionWithCallback() {
268 }
269
~HistoryFunctionWithCallback()270 HistoryFunctionWithCallback::~HistoryFunctionWithCallback() {
271 }
272
RunAsync()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
SendAsyncResponse()281 void HistoryFunctionWithCallback::SendAsyncResponse() {
282 base::MessageLoop::current()->PostTask(
283 FROM_HERE,
284 base::Bind(&HistoryFunctionWithCallback::SendResponseToCallback, this));
285 }
286
SendResponseToCallback()287 void HistoryFunctionWithCallback::SendResponseToCallback() {
288 SendResponse(true);
289 Release(); // Balanced in RunAsync().
290 }
291
RunAsyncImpl()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
QueryComplete(bool success,const history::URLRow & url_row,const history::VisitVector & visits)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
RunAsyncImpl()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, options, &cancelable_consumer_,
348 base::Bind(&HistorySearchFunction::SearchComplete,
349 base::Unretained(this)));
350
351 return true;
352 }
353
SearchComplete(HistoryService::Handle request_handle,history::QueryResults * results)354 void HistorySearchFunction::SearchComplete(
355 HistoryService::Handle request_handle,
356 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
RunAsync()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
RunAsync()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
RunAsyncImpl()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
DeleteComplete()448 void HistoryDeleteRangeFunction::DeleteComplete() {
449 SendAsyncResponse();
450 }
451
RunAsyncImpl()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
DeleteComplete()478 void HistoryDeleteAllFunction::DeleteComplete() {
479 SendAsyncResponse();
480 }
481
482 } // namespace extensions
483