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/json/json_writer.h"
     11 #include "base/lazy_instance.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/prefs/pref_service.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "base/time/time.h"
     18 #include "base/values.h"
     19 #include "chrome/browser/chrome_notification_types.h"
     20 #include "chrome/browser/extensions/event_router.h"
     21 #include "chrome/browser/extensions/extension_system.h"
     22 #include "chrome/browser/history/history_service.h"
     23 #include "chrome/browser/history/history_service_factory.h"
     24 #include "chrome/browser/history/history_types.h"
     25 #include "chrome/browser/history/visit_filter.h"
     26 #include "chrome/browser/profiles/profile.h"
     27 #include "chrome/common/cancelable_task_tracker.h"
     28 #include "chrome/common/extensions/api/experimental_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 
     33 namespace extensions {
     34 
     35 using api::experimental_history::MostVisitedItem;
     36 using api::history::HistoryItem;
     37 using api::history::VisitItem;
     38 
     39 typedef std::vector<linked_ptr<api::history::HistoryItem> >
     40     HistoryItemList;
     41 typedef std::vector<linked_ptr<api::history::VisitItem> >
     42     VisitItemList;
     43 
     44 namespace AddUrl = api::history::AddUrl;
     45 namespace DeleteUrl = api::history::DeleteUrl;
     46 namespace DeleteRange = api::history::DeleteRange;
     47 namespace GetMostVisited = api::experimental_history::GetMostVisited;
     48 namespace GetVisits = api::history::GetVisits;
     49 namespace OnVisited = api::history::OnVisited;
     50 namespace OnVisitRemoved = api::history::OnVisitRemoved;
     51 namespace Search = api::history::Search;
     52 
     53 namespace {
     54 
     55 const char kOnVisited[] = "history.onVisited";
     56 const char kOnVisitRemoved[] = "history.onVisitRemoved";
     57 
     58 const char kInvalidIdError[] = "History item id is invalid.";
     59 const char kInvalidUrlError[] = "Url is invalid.";
     60 const char kDeleteProhibitedError[] = "Browsing history is not allowed to be "
     61                                       "deleted.";
     62 
     63 double MilliSecondsFromTime(const base::Time& time) {
     64   return 1000 * time.ToDoubleT();
     65 }
     66 
     67 scoped_ptr<HistoryItem> GetHistoryItem(const history::URLRow& row) {
     68   scoped_ptr<HistoryItem> history_item(new HistoryItem());
     69 
     70   history_item->id = base::Int64ToString(row.id());
     71   history_item->url.reset(new std::string(row.url().spec()));
     72   history_item->title.reset(new std::string(UTF16ToUTF8(row.title())));
     73   history_item->last_visit_time.reset(
     74       new double(MilliSecondsFromTime(row.last_visit())));
     75   history_item->typed_count.reset(new int(row.typed_count()));
     76   history_item->visit_count.reset(new int(row.visit_count()));
     77 
     78   return history_item.Pass();
     79 }
     80 
     81 scoped_ptr<VisitItem> GetVisitItem(const history::VisitRow& row) {
     82   scoped_ptr<VisitItem> visit_item(new VisitItem());
     83 
     84   visit_item->id = base::Int64ToString(row.url_id);
     85   visit_item->visit_id = base::Int64ToString(row.visit_id);
     86   visit_item->visit_time.reset(
     87       new double(MilliSecondsFromTime(row.visit_time)));
     88   visit_item->referring_visit_id = base::Int64ToString(row.referring_visit);
     89 
     90   VisitItem::Transition transition = VisitItem::TRANSITION_LINK;
     91   switch (row.transition & content::PAGE_TRANSITION_CORE_MASK) {
     92     case content::PAGE_TRANSITION_LINK:
     93       transition = VisitItem::TRANSITION_LINK;
     94       break;
     95     case content::PAGE_TRANSITION_TYPED:
     96       transition = VisitItem::TRANSITION_TYPED;
     97       break;
     98     case content::PAGE_TRANSITION_AUTO_BOOKMARK:
     99       transition = VisitItem::TRANSITION_AUTO_BOOKMARK;
    100       break;
    101     case content::PAGE_TRANSITION_AUTO_SUBFRAME:
    102       transition = VisitItem::TRANSITION_AUTO_SUBFRAME;
    103       break;
    104     case content::PAGE_TRANSITION_MANUAL_SUBFRAME:
    105       transition = VisitItem::TRANSITION_MANUAL_SUBFRAME;
    106       break;
    107     case content::PAGE_TRANSITION_GENERATED:
    108       transition = VisitItem::TRANSITION_GENERATED;
    109       break;
    110     case content::PAGE_TRANSITION_AUTO_TOPLEVEL:
    111       transition = VisitItem::TRANSITION_AUTO_TOPLEVEL;
    112       break;
    113     case content::PAGE_TRANSITION_FORM_SUBMIT:
    114       transition = VisitItem::TRANSITION_FORM_SUBMIT;
    115       break;
    116     case content::PAGE_TRANSITION_RELOAD:
    117       transition = VisitItem::TRANSITION_RELOAD;
    118       break;
    119     case content::PAGE_TRANSITION_KEYWORD:
    120       transition = VisitItem::TRANSITION_KEYWORD;
    121       break;
    122     case content::PAGE_TRANSITION_KEYWORD_GENERATED:
    123       transition = VisitItem::TRANSITION_KEYWORD_GENERATED;
    124       break;
    125     default:
    126       DCHECK(false);
    127   }
    128 
    129   visit_item->transition = transition;
    130 
    131   return visit_item.Pass();
    132 }
    133 
    134 }  // namespace
    135 
    136 HistoryEventRouter::HistoryEventRouter(Profile* profile) {
    137   const content::Source<Profile> source = content::Source<Profile>(profile);
    138   registrar_.Add(this,
    139                  chrome::NOTIFICATION_HISTORY_URL_VISITED,
    140                  source);
    141   registrar_.Add(this,
    142                  chrome::NOTIFICATION_HISTORY_URLS_DELETED,
    143                  source);
    144 }
    145 
    146 HistoryEventRouter::~HistoryEventRouter() {}
    147 
    148 void HistoryEventRouter::Observe(int type,
    149                                  const content::NotificationSource& source,
    150                                  const content::NotificationDetails& details) {
    151   switch (type) {
    152     case chrome::NOTIFICATION_HISTORY_URL_VISITED:
    153       HistoryUrlVisited(
    154           content::Source<Profile>(source).ptr(),
    155           content::Details<const history::URLVisitedDetails>(details).ptr());
    156       break;
    157     case chrome::NOTIFICATION_HISTORY_URLS_DELETED:
    158       HistoryUrlsRemoved(
    159           content::Source<Profile>(source).ptr(),
    160           content::Details<const history::URLsDeletedDetails>(details).ptr());
    161       break;
    162     default:
    163       NOTREACHED();
    164   }
    165 }
    166 
    167 void HistoryEventRouter::HistoryUrlVisited(
    168     Profile* profile,
    169     const history::URLVisitedDetails* details) {
    170   scoped_ptr<HistoryItem> history_item = GetHistoryItem(details->row);
    171   scoped_ptr<base::ListValue> args = OnVisited::Create(*history_item);
    172 
    173   DispatchEvent(profile, kOnVisited, args.Pass());
    174 }
    175 
    176 void HistoryEventRouter::HistoryUrlsRemoved(
    177     Profile* profile,
    178     const history::URLsDeletedDetails* details) {
    179   OnVisitRemoved::Removed removed;
    180   removed.all_history = details->all_history;
    181 
    182   std::vector<std::string>* urls = new std::vector<std::string>();
    183   for (history::URLRows::const_iterator iterator = details->rows.begin();
    184       iterator != details->rows.end(); ++iterator) {
    185     urls->push_back(iterator->url().spec());
    186   }
    187   removed.urls.reset(urls);
    188 
    189   scoped_ptr<base::ListValue> args = OnVisitRemoved::Create(removed);
    190   DispatchEvent(profile, kOnVisitRemoved, args.Pass());
    191 }
    192 
    193 void HistoryEventRouter::DispatchEvent(
    194     Profile* profile,
    195     const char* event_name,
    196     scoped_ptr<base::ListValue> event_args) {
    197   if (profile && extensions::ExtensionSystem::Get(profile)->event_router()) {
    198     scoped_ptr<extensions::Event> event(new extensions::Event(
    199         event_name, event_args.Pass()));
    200     event->restrict_to_profile = profile;
    201     extensions::ExtensionSystem::Get(profile)->event_router()->
    202         BroadcastEvent(event.Pass());
    203   }
    204 }
    205 
    206 HistoryAPI::HistoryAPI(Profile* profile) : profile_(profile) {
    207   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
    208       this, kOnVisited);
    209   ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
    210       this, kOnVisitRemoved);
    211 }
    212 
    213 HistoryAPI::~HistoryAPI() {
    214 }
    215 
    216 void HistoryAPI::Shutdown() {
    217   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
    218 }
    219 
    220 static base::LazyInstance<ProfileKeyedAPIFactory<HistoryAPI> >
    221 g_factory = LAZY_INSTANCE_INITIALIZER;
    222 
    223 // static
    224 ProfileKeyedAPIFactory<HistoryAPI>* HistoryAPI::GetFactoryInstance() {
    225   return &g_factory.Get();
    226 }
    227 
    228 void HistoryAPI::OnListenerAdded(const EventListenerInfo& details) {
    229   history_event_router_.reset(new HistoryEventRouter(profile_));
    230   ExtensionSystem::Get(profile_)->event_router()->UnregisterObserver(this);
    231 }
    232 
    233 void HistoryFunction::Run() {
    234   if (!RunImpl()) {
    235     SendResponse(false);
    236   }
    237 }
    238 
    239 bool HistoryFunction::ValidateUrl(const std::string& url_string, GURL* url) {
    240   GURL temp_url(url_string);
    241   if (!temp_url.is_valid()) {
    242     error_ = kInvalidUrlError;
    243     return false;
    244   }
    245   url->Swap(&temp_url);
    246   return true;
    247 }
    248 
    249 bool HistoryFunction::VerifyDeleteAllowed() {
    250   PrefService* prefs = profile()->GetPrefs();
    251   if (!prefs->GetBoolean(prefs::kAllowDeletingBrowserHistory)) {
    252     error_ = kDeleteProhibitedError;
    253     return false;
    254   }
    255   return true;
    256 }
    257 
    258 base::Time HistoryFunction::GetTime(double ms_from_epoch) {
    259   // The history service has seconds resolution, while javascript Date() has
    260   // milliseconds resolution.
    261   double seconds_from_epoch = ms_from_epoch / 1000.0;
    262   // Time::FromDoubleT converts double time 0 to empty Time object. So we need
    263   // to do special handling here.
    264   return (seconds_from_epoch == 0) ?
    265       base::Time::UnixEpoch() : base::Time::FromDoubleT(seconds_from_epoch);
    266 }
    267 
    268 HistoryFunctionWithCallback::HistoryFunctionWithCallback() {
    269 }
    270 
    271 HistoryFunctionWithCallback::~HistoryFunctionWithCallback() {
    272 }
    273 
    274 bool HistoryFunctionWithCallback::RunImpl() {
    275   AddRef();  // Balanced in SendAysncRepose() and below.
    276   bool retval = RunAsyncImpl();
    277   if (false == retval)
    278     Release();
    279   return retval;
    280 }
    281 
    282 void HistoryFunctionWithCallback::SendAsyncResponse() {
    283   base::MessageLoop::current()->PostTask(
    284       FROM_HERE,
    285       base::Bind(&HistoryFunctionWithCallback::SendResponseToCallback, this));
    286 }
    287 
    288 void HistoryFunctionWithCallback::SendResponseToCallback() {
    289   SendResponse(true);
    290   Release();  // Balanced in RunImpl().
    291 }
    292 
    293 bool HistoryGetMostVisitedFunction::RunAsyncImpl() {
    294   scoped_ptr<GetMostVisited::Params> params =
    295       GetMostVisited::Params::Create(*args_);
    296   EXTENSION_FUNCTION_VALIDATE(params.get());
    297 
    298   history::VisitFilter filter;
    299   if (params->details.filter_time.get())
    300     filter.SetFilterTime(GetTime(*params->details.filter_time));
    301   if (params->details.filter_width.get()) {
    302     filter.SetFilterWidth(base::TimeDelta::FromMilliseconds(
    303         static_cast<int64>(*params->details.filter_width)));
    304   }
    305   if (params->details.day_of_the_week.get())
    306     filter.SetDayOfTheWeekFilter(*params->details.day_of_the_week);
    307   int max_results = 100;
    308   if (params->details.max_results.get())
    309     max_results = *params->details.max_results;
    310   HistoryService* hs =
    311       HistoryServiceFactory::GetForProfile(profile(), Profile::EXPLICIT_ACCESS);
    312   hs->QueryFilteredURLs(max_results, filter, false, &cancelable_consumer_,
    313       base::Bind(&HistoryGetMostVisitedFunction::QueryComplete,
    314                  base::Unretained(this)));
    315   return true;
    316 }
    317 
    318 void HistoryGetMostVisitedFunction::QueryComplete(
    319     CancelableRequestProvider::Handle handle,
    320     const history::FilteredURLList& data) {
    321   std::vector<linked_ptr<MostVisitedItem> > results;
    322   results.reserve(data.size());
    323   for (size_t i = 0; i < data.size(); i++) {
    324     linked_ptr<MostVisitedItem> item(new MostVisitedItem);
    325     item->url = data[i].url.spec();
    326     item->title = UTF16ToUTF8(data[i].title);
    327     results.push_back(item);
    328   }
    329   results_ = GetMostVisited::Results::Create(results);
    330   SendAsyncResponse();
    331 }
    332 
    333 bool HistoryGetVisitsFunction::RunAsyncImpl() {
    334   scoped_ptr<GetVisits::Params> params(GetVisits::Params::Create(*args_));
    335   EXTENSION_FUNCTION_VALIDATE(params.get());
    336 
    337   GURL url;
    338   if (!ValidateUrl(params->details.url, &url))
    339     return false;
    340 
    341   HistoryService* hs =
    342       HistoryServiceFactory::GetForProfile(profile(),
    343                                            Profile::EXPLICIT_ACCESS);
    344   hs->QueryURL(url,
    345                true,  // Retrieve full history of a URL.
    346                &cancelable_consumer_,
    347                base::Bind(&HistoryGetVisitsFunction::QueryComplete,
    348                           base::Unretained(this)));
    349 
    350   return true;
    351 }
    352 
    353 void HistoryGetVisitsFunction::QueryComplete(
    354     HistoryService::Handle request_service,
    355     bool success,
    356     const history::URLRow* url_row,
    357     history::VisitVector* visits) {
    358   VisitItemList visit_item_vec;
    359   if (visits && !visits->empty()) {
    360     for (history::VisitVector::iterator iterator = visits->begin();
    361          iterator != visits->end();
    362          ++iterator) {
    363       visit_item_vec.push_back(make_linked_ptr(
    364           GetVisitItem(*iterator).release()));
    365     }
    366   }
    367 
    368   results_ = GetVisits::Results::Create(visit_item_vec);
    369   SendAsyncResponse();
    370 }
    371 
    372 bool HistorySearchFunction::RunAsyncImpl() {
    373   scoped_ptr<Search::Params> params(Search::Params::Create(*args_));
    374   EXTENSION_FUNCTION_VALIDATE(params.get());
    375 
    376   string16 search_text = UTF8ToUTF16(params->query.text);
    377 
    378   history::QueryOptions options;
    379   options.SetRecentDayRange(1);
    380   options.max_count = 100;
    381 
    382   if (params->query.start_time.get())
    383     options.begin_time = GetTime(*params->query.start_time);
    384   if (params->query.end_time.get())
    385     options.end_time = GetTime(*params->query.end_time);
    386   if (params->query.max_results.get())
    387     options.max_count = *params->query.max_results;
    388 
    389   HistoryService* hs =
    390       HistoryServiceFactory::GetForProfile(profile(),
    391                                            Profile::EXPLICIT_ACCESS);
    392   hs->QueryHistory(search_text, options, &cancelable_consumer_,
    393                    base::Bind(&HistorySearchFunction::SearchComplete,
    394                               base::Unretained(this)));
    395 
    396   return true;
    397 }
    398 
    399 void HistorySearchFunction::SearchComplete(
    400     HistoryService::Handle request_handle,
    401     history::QueryResults* results) {
    402   HistoryItemList history_item_vec;
    403   if (results && !results->empty()) {
    404     for (history::QueryResults::URLResultVector::const_iterator iterator =
    405             results->begin();
    406          iterator != results->end();
    407         ++iterator) {
    408       history_item_vec.push_back(make_linked_ptr(
    409           GetHistoryItem(**iterator).release()));
    410     }
    411   }
    412   results_ = Search::Results::Create(history_item_vec);
    413   SendAsyncResponse();
    414 }
    415 
    416 bool HistoryAddUrlFunction::RunImpl() {
    417   scoped_ptr<AddUrl::Params> params(AddUrl::Params::Create(*args_));
    418   EXTENSION_FUNCTION_VALIDATE(params.get());
    419 
    420   GURL url;
    421   if (!ValidateUrl(params->details.url, &url))
    422     return false;
    423 
    424   HistoryService* hs =
    425       HistoryServiceFactory::GetForProfile(profile(),
    426                                            Profile::EXPLICIT_ACCESS);
    427   hs->AddPage(url, base::Time::Now(), history::SOURCE_EXTENSION);
    428 
    429   SendResponse(true);
    430   return true;
    431 }
    432 
    433 bool HistoryDeleteUrlFunction::RunImpl() {
    434   scoped_ptr<DeleteUrl::Params> params(DeleteUrl::Params::Create(*args_));
    435   EXTENSION_FUNCTION_VALIDATE(params.get());
    436 
    437   if (!VerifyDeleteAllowed())
    438     return false;
    439 
    440   GURL url;
    441   if (!ValidateUrl(params->details.url, &url))
    442     return false;
    443 
    444   HistoryService* hs =
    445       HistoryServiceFactory::GetForProfile(profile(),
    446                                            Profile::EXPLICIT_ACCESS);
    447   hs->DeleteURL(url);
    448 
    449   SendResponse(true);
    450   return true;
    451 }
    452 
    453 bool HistoryDeleteRangeFunction::RunAsyncImpl() {
    454   scoped_ptr<DeleteRange::Params> params(DeleteRange::Params::Create(*args_));
    455   EXTENSION_FUNCTION_VALIDATE(params.get());
    456 
    457   if (!VerifyDeleteAllowed())
    458     return false;
    459 
    460   base::Time start_time = GetTime(params->range.start_time);
    461   base::Time end_time = GetTime(params->range.end_time);
    462 
    463   std::set<GURL> restrict_urls;
    464   HistoryService* hs =
    465       HistoryServiceFactory::GetForProfile(profile(),
    466                                            Profile::EXPLICIT_ACCESS);
    467   hs->ExpireHistoryBetween(
    468       restrict_urls,
    469       start_time,
    470       end_time,
    471       base::Bind(&HistoryDeleteRangeFunction::DeleteComplete,
    472                  base::Unretained(this)),
    473       &task_tracker_);
    474 
    475   return true;
    476 }
    477 
    478 void HistoryDeleteRangeFunction::DeleteComplete() {
    479   SendAsyncResponse();
    480 }
    481 
    482 bool HistoryDeleteAllFunction::RunAsyncImpl() {
    483   if (!VerifyDeleteAllowed())
    484     return false;
    485 
    486   std::set<GURL> restrict_urls;
    487   HistoryService* hs =
    488       HistoryServiceFactory::GetForProfile(profile(),
    489                                            Profile::EXPLICIT_ACCESS);
    490   hs->ExpireHistoryBetween(
    491       restrict_urls,
    492       base::Time(),      // Unbounded beginning...
    493       base::Time(),      // ...and the end.
    494       base::Bind(&HistoryDeleteAllFunction::DeleteComplete,
    495                  base::Unretained(this)),
    496       &task_tracker_);
    497 
    498   return true;
    499 }
    500 
    501 void HistoryDeleteAllFunction::DeleteComplete() {
    502   SendAsyncResponse();
    503 }
    504 
    505 }  // namespace extensions
    506