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/extension_system.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/cancelable_task_tracker.h" 28 #include "chrome/common/chrome_switches.h" 29 #include "chrome/common/extensions/api/history.h" 30 #include "chrome/common/pref_names.h" 31 #include "content/public/browser/notification_details.h" 32 #include "content/public/browser/notification_source.h" 33 #include "extensions/browser/event_router.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(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.Get(); 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 = 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 488