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/profiles/profile.h" 26 #include "chrome/common/chrome_switches.h" 27 #include "chrome/common/extensions/api/history.h" 28 #include "chrome/common/pref_names.h" 29 #include "components/history/core/browser/history_types.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 & ui::PAGE_TRANSITION_CORE_MASK) { 90 case ui::PAGE_TRANSITION_LINK: 91 transition = VisitItem::TRANSITION_LINK; 92 break; 93 case ui::PAGE_TRANSITION_TYPED: 94 transition = VisitItem::TRANSITION_TYPED; 95 break; 96 case ui::PAGE_TRANSITION_AUTO_BOOKMARK: 97 transition = VisitItem::TRANSITION_AUTO_BOOKMARK; 98 break; 99 case ui::PAGE_TRANSITION_AUTO_SUBFRAME: 100 transition = VisitItem::TRANSITION_AUTO_SUBFRAME; 101 break; 102 case ui::PAGE_TRANSITION_MANUAL_SUBFRAME: 103 transition = VisitItem::TRANSITION_MANUAL_SUBFRAME; 104 break; 105 case ui::PAGE_TRANSITION_GENERATED: 106 transition = VisitItem::TRANSITION_GENERATED; 107 break; 108 case ui::PAGE_TRANSITION_AUTO_TOPLEVEL: 109 transition = VisitItem::TRANSITION_AUTO_TOPLEVEL; 110 break; 111 case ui::PAGE_TRANSITION_FORM_SUBMIT: 112 transition = VisitItem::TRANSITION_FORM_SUBMIT; 113 break; 114 case ui::PAGE_TRANSITION_RELOAD: 115 transition = VisitItem::TRANSITION_RELOAD; 116 break; 117 case ui::PAGE_TRANSITION_KEYWORD: 118 transition = VisitItem::TRANSITION_KEYWORD; 119 break; 120 case ui::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 483