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