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