Home | History | Annotate | Download | only in history
      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