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