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