Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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/extension_history_api.h"
      6 
      7 #include "base/callback.h"
      8 #include "base/json/json_writer.h"
      9 #include "base/message_loop.h"
     10 #include "base/string_number_conversions.h"
     11 #include "base/task.h"
     12 #include "base/values.h"
     13 #include "chrome/browser/extensions/extension_event_router.h"
     14 #include "chrome/browser/extensions/extension_history_api_constants.h"
     15 #include "chrome/browser/history/history.h"
     16 #include "chrome/browser/history/history_types.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "content/common/notification_service.h"
     19 #include "content/common/notification_type.h"
     20 
     21 namespace keys = extension_history_api_constants;
     22 
     23 namespace {
     24 
     25 double MilliSecondsFromTime(const base::Time& time) {
     26   return 1000 * time.ToDoubleT();
     27 }
     28 
     29 void GetHistoryItemDictionary(const history::URLRow& row,
     30                               DictionaryValue* value) {
     31   value->SetString(keys::kIdKey, base::Int64ToString(row.id()));
     32   value->SetString(keys::kUrlKey, row.url().spec());
     33   value->SetString(keys::kTitleKey, row.title());
     34   value->SetDouble(keys::kLastVisitdKey,
     35                    MilliSecondsFromTime(row.last_visit()));
     36   value->SetInteger(keys::kTypedCountKey, row.typed_count());
     37   value->SetInteger(keys::kVisitCountKey, row.visit_count());
     38 }
     39 
     40 void AddHistoryNode(const history::URLRow& row, ListValue* list) {
     41   DictionaryValue* dict = new DictionaryValue();
     42   GetHistoryItemDictionary(row, dict);
     43   list->Append(dict);
     44 }
     45 
     46 void GetVisitInfoDictionary(const history::VisitRow& row,
     47                             DictionaryValue* value) {
     48   value->SetString(keys::kIdKey, base::Int64ToString(row.url_id));
     49   value->SetString(keys::kVisitId, base::Int64ToString(row.visit_id));
     50   value->SetDouble(keys::kVisitTime, MilliSecondsFromTime(row.visit_time));
     51   value->SetString(keys::kReferringVisitId,
     52                    base::Int64ToString(row.referring_visit));
     53 
     54   const char* trans = PageTransition::CoreTransitionString(row.transition);
     55   DCHECK(trans) << "Invalid transition.";
     56   value->SetString(keys::kTransition, trans);
     57 }
     58 
     59 void AddVisitNode(const history::VisitRow& row, ListValue* list) {
     60   DictionaryValue* dict = new DictionaryValue();
     61   GetVisitInfoDictionary(row, dict);
     62   list->Append(dict);
     63 }
     64 
     65 }  // namespace
     66 
     67 ExtensionHistoryEventRouter* ExtensionHistoryEventRouter::GetInstance() {
     68   return Singleton<ExtensionHistoryEventRouter>::get();
     69 }
     70 
     71 void ExtensionHistoryEventRouter::ObserveProfile(Profile* profile) {
     72   NotificationSource source = Source<Profile>(profile);
     73   if (profiles_.find(source.map_key()) == profiles_.end())
     74     profiles_[source.map_key()] = profile;
     75 
     76   if (registrar_.IsEmpty()) {
     77     registrar_.Add(this,
     78                    NotificationType::HISTORY_URL_VISITED,
     79                    NotificationService::AllSources());
     80     registrar_.Add(this,
     81                    NotificationType::HISTORY_URLS_DELETED,
     82                    NotificationService::AllSources());
     83   }
     84 }
     85 
     86 ExtensionHistoryEventRouter::ExtensionHistoryEventRouter() {}
     87 
     88 ExtensionHistoryEventRouter::~ExtensionHistoryEventRouter() {}
     89 
     90 void ExtensionHistoryEventRouter::Observe(NotificationType type,
     91                                           const NotificationSource& source,
     92                                           const NotificationDetails& details) {
     93   ProfileMap::iterator it = profiles_.find(source.map_key());
     94   if (it != profiles_.end()) {
     95     Profile* profile = it->second;
     96     switch (type.value) {
     97       case NotificationType::HISTORY_URL_VISITED:
     98         HistoryUrlVisited(
     99             profile,
    100             Details<const history::URLVisitedDetails>(details).ptr());
    101         break;
    102       case NotificationType::HISTORY_URLS_DELETED:
    103         HistoryUrlsRemoved(
    104             profile,
    105             Details<const history::URLsDeletedDetails>(details).ptr());
    106         break;
    107       default:
    108         NOTREACHED();
    109     }
    110   }
    111 }
    112 
    113 void ExtensionHistoryEventRouter::HistoryUrlVisited(
    114     Profile* profile,
    115     const history::URLVisitedDetails* details) {
    116   ListValue args;
    117   DictionaryValue* dict = new DictionaryValue();
    118   GetHistoryItemDictionary(details->row, dict);
    119   args.Append(dict);
    120 
    121   std::string json_args;
    122   base::JSONWriter::Write(&args, false, &json_args);
    123   DispatchEvent(profile, keys::kOnVisited, json_args);
    124 }
    125 
    126 void ExtensionHistoryEventRouter::HistoryUrlsRemoved(
    127     Profile* profile,
    128     const history::URLsDeletedDetails* details) {
    129   ListValue args;
    130   DictionaryValue* dict = new DictionaryValue();
    131   dict->SetBoolean(keys::kAllHistoryKey, details->all_history);
    132   ListValue* urls = new ListValue();
    133   for (std::set<GURL>::const_iterator iterator = details->urls.begin();
    134       iterator != details->urls.end();
    135       ++iterator) {
    136     urls->Append(new StringValue(iterator->spec()));
    137   }
    138   dict->Set(keys::kUrlsKey, urls);
    139   args.Append(dict);
    140 
    141   std::string json_args;
    142   base::JSONWriter::Write(&args, false, &json_args);
    143   DispatchEvent(profile, keys::kOnVisitRemoved, json_args);
    144 }
    145 
    146 void ExtensionHistoryEventRouter::DispatchEvent(Profile* profile,
    147                                                 const char* event_name,
    148                                                 const std::string& json_args) {
    149   if (profile && profile->GetExtensionEventRouter()) {
    150     profile->GetExtensionEventRouter()->DispatchEventToRenderers(
    151         event_name, json_args, profile, GURL());
    152   }
    153 }
    154 
    155 void HistoryFunction::Run() {
    156   if (!RunImpl()) {
    157     SendResponse(false);
    158   }
    159 }
    160 
    161 bool HistoryFunction::GetUrlFromValue(Value* value, GURL* url) {
    162   std::string url_string;
    163   if (!value->GetAsString(&url_string)) {
    164     bad_message_ = true;
    165     return false;
    166   }
    167 
    168   GURL temp_url(url_string);
    169   if (!temp_url.is_valid()) {
    170     error_ = keys::kInvalidUrlError;
    171     return false;
    172   }
    173   url->Swap(&temp_url);
    174   return true;
    175 }
    176 
    177 bool HistoryFunction::GetTimeFromValue(Value* value, base::Time* time) {
    178   double ms_from_epoch = 0.0;
    179   if (!value->GetAsDouble(&ms_from_epoch)) {
    180     int ms_from_epoch_as_int = 0;
    181     if (!value->GetAsInteger(&ms_from_epoch_as_int))
    182       return false;
    183     ms_from_epoch = static_cast<double>(ms_from_epoch_as_int);
    184   }
    185   // The history service has seconds resolution, while javascript Date() has
    186   // milliseconds resolution.
    187   double seconds_from_epoch = ms_from_epoch / 1000.0;
    188   // Time::FromDoubleT converts double time 0 to empty Time object. So we need
    189   // to do special handling here.
    190   *time = (seconds_from_epoch == 0) ?
    191       base::Time::UnixEpoch() : base::Time::FromDoubleT(seconds_from_epoch);
    192   return true;
    193 }
    194 
    195 HistoryFunctionWithCallback::HistoryFunctionWithCallback() {
    196 }
    197 
    198 HistoryFunctionWithCallback::~HistoryFunctionWithCallback() {
    199 }
    200 
    201 bool HistoryFunctionWithCallback::RunImpl() {
    202   AddRef();  // Balanced in SendAysncRepose() and below.
    203   bool retval = RunAsyncImpl();
    204   if (false == retval)
    205     Release();
    206   return retval;
    207 }
    208 
    209 void HistoryFunctionWithCallback::SendAsyncResponse() {
    210   MessageLoop::current()->PostTask(
    211       FROM_HERE,
    212       NewRunnableMethod(
    213           this,
    214           &HistoryFunctionWithCallback::SendResponseToCallback));
    215 }
    216 
    217 void HistoryFunctionWithCallback::SendResponseToCallback() {
    218   SendResponse(true);
    219   Release();  // Balanced in RunImpl().
    220 }
    221 
    222 bool GetVisitsHistoryFunction::RunAsyncImpl() {
    223   DictionaryValue* json;
    224   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
    225 
    226   Value* value;
    227   EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kUrlKey, &value));
    228 
    229   GURL url;
    230   if (!GetUrlFromValue(value, &url))
    231     return false;
    232 
    233   HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
    234   hs->QueryURL(url,
    235                true,  // Retrieve full history of a URL.
    236                &cancelable_consumer_,
    237                NewCallback(this, &GetVisitsHistoryFunction::QueryComplete));
    238 
    239   return true;
    240 }
    241 
    242 void GetVisitsHistoryFunction::QueryComplete(
    243     HistoryService::Handle request_service,
    244     bool success,
    245     const history::URLRow* url_row,
    246     history::VisitVector* visits) {
    247   ListValue* list = new ListValue();
    248   if (visits && !visits->empty()) {
    249     for (history::VisitVector::iterator iterator = visits->begin();
    250          iterator != visits->end();
    251          ++iterator) {
    252       AddVisitNode(*iterator, list);
    253     }
    254   }
    255   result_.reset(list);
    256   SendAsyncResponse();
    257 }
    258 
    259 bool SearchHistoryFunction::RunAsyncImpl() {
    260   DictionaryValue* json;
    261   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
    262 
    263   // Initialize the HistoryQuery
    264   string16 search_text;
    265   EXTENSION_FUNCTION_VALIDATE(json->GetString(keys::kTextKey, &search_text));
    266 
    267   history::QueryOptions options;
    268   options.SetRecentDayRange(1);
    269   options.max_count = 100;
    270 
    271   if (json->HasKey(keys::kStartTimeKey)) {  // Optional.
    272     Value* value;
    273     EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kStartTimeKey, &value));
    274     EXTENSION_FUNCTION_VALIDATE(GetTimeFromValue(value, &options.begin_time));
    275   }
    276   if (json->HasKey(keys::kEndTimeKey)) {  // Optional.
    277     Value* value;
    278     EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kEndTimeKey, &value));
    279     EXTENSION_FUNCTION_VALIDATE(GetTimeFromValue(value, &options.end_time));
    280   }
    281   if (json->HasKey(keys::kMaxResultsKey)) {  // Optional.
    282     EXTENSION_FUNCTION_VALIDATE(json->GetInteger(keys::kMaxResultsKey,
    283                                                  &options.max_count));
    284   }
    285 
    286   HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
    287   hs->QueryHistory(search_text, options, &cancelable_consumer_,
    288                    NewCallback(this, &SearchHistoryFunction::SearchComplete));
    289 
    290   return true;
    291 }
    292 
    293 void SearchHistoryFunction::SearchComplete(
    294     HistoryService::Handle request_handle,
    295     history::QueryResults* results) {
    296   ListValue* list = new ListValue();
    297   if (results && !results->empty()) {
    298     for (history::QueryResults::URLResultVector::const_iterator iterator =
    299             results->begin();
    300          iterator != results->end();
    301         ++iterator) {
    302       AddHistoryNode(**iterator, list);
    303     }
    304   }
    305   result_.reset(list);
    306   SendAsyncResponse();
    307 }
    308 
    309 bool AddUrlHistoryFunction::RunImpl() {
    310   DictionaryValue* json;
    311   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
    312 
    313   Value* value;
    314   EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kUrlKey, &value));
    315 
    316   GURL url;
    317   if (!GetUrlFromValue(value, &url))
    318     return false;
    319 
    320   HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
    321   hs->AddPage(url, history::SOURCE_EXTENSION);
    322 
    323   SendResponse(true);
    324   return true;
    325 }
    326 
    327 bool DeleteUrlHistoryFunction::RunImpl() {
    328   DictionaryValue* json;
    329   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
    330 
    331   Value* value;
    332   EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kUrlKey, &value));
    333 
    334   GURL url;
    335   if (!GetUrlFromValue(value, &url))
    336     return false;
    337 
    338   HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
    339   hs->DeleteURL(url);
    340 
    341   SendResponse(true);
    342   return true;
    343 }
    344 
    345 bool DeleteRangeHistoryFunction::RunAsyncImpl() {
    346   DictionaryValue* json;
    347   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
    348 
    349   Value* value = NULL;
    350   EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kStartTimeKey, &value));
    351   base::Time begin_time;
    352   EXTENSION_FUNCTION_VALIDATE(GetTimeFromValue(value, &begin_time));
    353 
    354   EXTENSION_FUNCTION_VALIDATE(json->Get(keys::kEndTimeKey, &value));
    355   base::Time end_time;
    356   EXTENSION_FUNCTION_VALIDATE(GetTimeFromValue(value, &end_time));
    357 
    358   std::set<GURL> restrict_urls;
    359   HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
    360   hs->ExpireHistoryBetween(
    361       restrict_urls,
    362       begin_time,
    363       end_time,
    364       &cancelable_consumer_,
    365       NewCallback(this, &DeleteRangeHistoryFunction::DeleteComplete));
    366 
    367   return true;
    368 }
    369 
    370 void DeleteRangeHistoryFunction::DeleteComplete() {
    371   SendAsyncResponse();
    372 }
    373 
    374 bool DeleteAllHistoryFunction::RunAsyncImpl() {
    375   std::set<GURL> restrict_urls;
    376   HistoryService* hs = profile()->GetHistoryService(Profile::EXPLICIT_ACCESS);
    377   hs->ExpireHistoryBetween(
    378       restrict_urls,
    379       base::Time::UnixEpoch(),     // From the beginning of the epoch.
    380       base::Time::Now(),           // To the current time.
    381       &cancelable_consumer_,
    382       NewCallback(this, &DeleteAllHistoryFunction::DeleteComplete));
    383 
    384   return true;
    385 }
    386 
    387 void DeleteAllHistoryFunction::DeleteComplete() {
    388   SendAsyncResponse();
    389 }
    390