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/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