Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(487)

Side by Side Diff: chrome/browser/extensions/extension_bookmarks_module.cc

Issue 8374023: Move bookmarks extension api implementation to bookmarks dir (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix deps, rebase Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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_bookmarks_module.h"
6
7 #include "base/bind.h"
8 #include "base/file_path.h"
9 #include "base/i18n/file_util_icu.h"
10 #include "base/i18n/time_formatting.h"
11 #include "base/json/json_writer.h"
12 #include "base/path_service.h"
13 #include "base/sha1.h"
14 #include "base/stl_util.h"
15 #include "base/string16.h"
16 #include "base/string_number_conversions.h"
17 #include "base/string_util.h"
18 #include "base/time.h"
19 #include "base/utf_string_conversions.h"
20 #include "chrome/browser/bookmarks/bookmark_codec.h"
21 #include "chrome/browser/bookmarks/bookmark_html_writer.h"
22 #include "chrome/browser/bookmarks/bookmark_model.h"
23 #include "chrome/browser/bookmarks/bookmark_utils.h"
24 #include "chrome/browser/extensions/extension_bookmark_helpers.h"
25 #include "chrome/browser/extensions/extension_bookmarks_module_constants.h"
26 #include "chrome/browser/extensions/extension_event_router.h"
27 #include "chrome/browser/extensions/extension_function_dispatcher.h"
28 #include "chrome/browser/extensions/extensions_quota_service.h"
29 #include "chrome/browser/importer/importer_data_types.h"
30 #include "chrome/browser/importer/importer_host.h"
31 #include "chrome/browser/prefs/pref_service.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/ui/browser_list.h"
34 #include "chrome/common/chrome_notification_types.h"
35 #include "chrome/common/chrome_paths.h"
36 #include "chrome/common/pref_names.h"
37 #include "content/public/browser/notification_service.h"
38 #include "grit/generated_resources.h"
39 #include "ui/base/l10n/l10n_util.h"
40
41 namespace keys = extension_bookmarks_module_constants;
42
43 using base::TimeDelta;
44 typedef QuotaLimitHeuristic::Bucket Bucket;
45 typedef QuotaLimitHeuristic::Config Config;
46 typedef QuotaLimitHeuristic::BucketList BucketList;
47 typedef ExtensionsQuotaService::TimedLimit TimedLimit;
48 typedef ExtensionsQuotaService::SustainedLimit SustainedLimit;
49 typedef QuotaLimitHeuristic::BucketMapper BucketMapper;
50
51 namespace {
52
53 // Generates a default path (including a default filename) that will be
54 // used for pre-populating the "Export Bookmarks" file chooser dialog box.
55 FilePath GetDefaultFilepathForBookmarkExport() {
56 base::Time time = base::Time::Now();
57
58 // Concatenate a date stamp to the filename.
59 #if defined(OS_POSIX)
60 FilePath::StringType filename =
61 l10n_util::GetStringFUTF8(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME,
62 base::TimeFormatShortDateNumeric(time));
63 #elif defined(OS_WIN)
64 FilePath::StringType filename =
65 l10n_util::GetStringFUTF16(IDS_EXPORT_BOOKMARKS_DEFAULT_FILENAME,
66 base::TimeFormatShortDateNumeric(time));
67 #endif
68
69 file_util::ReplaceIllegalCharactersInPath(&filename, '_');
70
71 FilePath default_path;
72 PathService::Get(chrome::DIR_USER_DOCUMENTS, &default_path);
73 return default_path.Append(filename);
74 }
75
76 } // namespace
77
78 void BookmarksFunction::Run() {
79 BookmarkModel* model = profile()->GetBookmarkModel();
80 if (!model->IsLoaded()) {
81 // Bookmarks are not ready yet. We'll wait.
82 registrar_.Add(
83 this, chrome::NOTIFICATION_BOOKMARK_MODEL_LOADED,
84 content::NotificationService::AllBrowserContextsAndSources());
85 AddRef(); // Balanced in Observe().
86 return;
87 }
88
89 bool success = RunImpl();
90 if (success) {
91 content::NotificationService::current()->Notify(
92 chrome::NOTIFICATION_EXTENSION_BOOKMARKS_API_INVOKED,
93 content::Source<const Extension>(GetExtension()),
94 content::Details<const BookmarksFunction>(this));
95 }
96 SendResponse(success);
97 }
98
99 bool BookmarksFunction::GetBookmarkIdAsInt64(
100 const std::string& id_string, int64* id) {
101 if (base::StringToInt64(id_string, id))
102 return true;
103
104 error_ = keys::kInvalidIdError;
105 return false;
106 }
107
108 bool BookmarksFunction::EditBookmarksEnabled() {
109 if (profile_->GetPrefs()->GetBoolean(prefs::kEditBookmarksEnabled))
110 return true;
111 error_ = keys::kEditBookmarksDisabled;
112 return false;
113 }
114
115 void BookmarksFunction::Observe(int type,
116 const content::NotificationSource& source,
117 const content::NotificationDetails& details) {
118 DCHECK(type == chrome::NOTIFICATION_BOOKMARK_MODEL_LOADED);
119 Profile* source_profile = content::Source<Profile>(source).ptr();
120 if (!source_profile || !source_profile->IsSameProfile(profile()))
121 return;
122
123 DCHECK(profile()->GetBookmarkModel()->IsLoaded());
124 Run();
125 Release(); // Balanced in Run().
126 }
127
128 ExtensionBookmarkEventRouter::ExtensionBookmarkEventRouter(
129 BookmarkModel* model) : model_(model) {
130 }
131
132 ExtensionBookmarkEventRouter::~ExtensionBookmarkEventRouter() {
133 if (model_) {
134 model_->RemoveObserver(this);
135 }
136 }
137
138 void ExtensionBookmarkEventRouter::Init() {
139 model_->AddObserver(this);
140 }
141
142 void ExtensionBookmarkEventRouter::DispatchEvent(Profile *profile,
143 const char* event_name,
144 const std::string& json_args) {
145 if (profile->GetExtensionEventRouter()) {
146 profile->GetExtensionEventRouter()->DispatchEventToRenderers(
147 event_name, json_args, NULL, GURL());
148 }
149 }
150
151 void ExtensionBookmarkEventRouter::Loaded(BookmarkModel* model,
152 bool ids_reassigned) {
153 // TODO(erikkay): Perhaps we should send this event down to the extension
154 // so they know when it's safe to use the API?
155 }
156
157 void ExtensionBookmarkEventRouter::BookmarkModelBeingDeleted(
158 BookmarkModel* model) {
159 model_ = NULL;
160 }
161
162 void ExtensionBookmarkEventRouter::BookmarkNodeMoved(
163 BookmarkModel* model,
164 const BookmarkNode* old_parent,
165 int old_index,
166 const BookmarkNode* new_parent,
167 int new_index) {
168 ListValue args;
169 const BookmarkNode* node = new_parent->GetChild(new_index);
170 args.Append(new StringValue(base::Int64ToString(node->id())));
171 DictionaryValue* object_args = new DictionaryValue();
172 object_args->SetString(keys::kParentIdKey,
173 base::Int64ToString(new_parent->id()));
174 object_args->SetInteger(keys::kIndexKey, new_index);
175 object_args->SetString(keys::kOldParentIdKey,
176 base::Int64ToString(old_parent->id()));
177 object_args->SetInteger(keys::kOldIndexKey, old_index);
178 args.Append(object_args);
179
180 std::string json_args;
181 base::JSONWriter::Write(&args, false, &json_args);
182 DispatchEvent(model->profile(), keys::kOnBookmarkMoved, json_args);
183 }
184
185 void ExtensionBookmarkEventRouter::BookmarkNodeAdded(BookmarkModel* model,
186 const BookmarkNode* parent,
187 int index) {
188 ListValue args;
189 const BookmarkNode* node = parent->GetChild(index);
190 args.Append(new StringValue(base::Int64ToString(node->id())));
191 DictionaryValue* obj =
192 extension_bookmark_helpers::GetNodeDictionary(node, false, false);
193 args.Append(obj);
194
195 std::string json_args;
196 base::JSONWriter::Write(&args, false, &json_args);
197 DispatchEvent(model->profile(), keys::kOnBookmarkCreated, json_args);
198 }
199
200 void ExtensionBookmarkEventRouter::BookmarkNodeRemoved(
201 BookmarkModel* model,
202 const BookmarkNode* parent,
203 int index,
204 const BookmarkNode* node) {
205 ListValue args;
206 args.Append(new StringValue(base::Int64ToString(node->id())));
207 DictionaryValue* object_args = new DictionaryValue();
208 object_args->SetString(keys::kParentIdKey,
209 base::Int64ToString(parent->id()));
210 object_args->SetInteger(keys::kIndexKey, index);
211 args.Append(object_args);
212
213 std::string json_args;
214 base::JSONWriter::Write(&args, false, &json_args);
215 DispatchEvent(model->profile(), keys::kOnBookmarkRemoved, json_args);
216 }
217
218 void ExtensionBookmarkEventRouter::BookmarkNodeChanged(
219 BookmarkModel* model, const BookmarkNode* node) {
220 ListValue args;
221 args.Append(new StringValue(base::Int64ToString(node->id())));
222
223 // TODO(erikkay) The only three things that BookmarkModel sends this
224 // notification for are title, url and favicon. Since we're currently
225 // ignoring favicon and since the notification doesn't say which one anyway,
226 // for now we only include title and url. The ideal thing would be to change
227 // BookmarkModel to indicate what changed.
228 DictionaryValue* object_args = new DictionaryValue();
229 object_args->SetString(keys::kTitleKey, node->GetTitle());
230 if (node->is_url())
231 object_args->SetString(keys::kUrlKey, node->url().spec());
232 args.Append(object_args);
233
234 std::string json_args;
235 base::JSONWriter::Write(&args, false, &json_args);
236 DispatchEvent(model->profile(), keys::kOnBookmarkChanged, json_args);
237 }
238
239 void ExtensionBookmarkEventRouter::BookmarkNodeFaviconChanged(
240 BookmarkModel* model, const BookmarkNode* node) {
241 // TODO(erikkay) anything we should do here?
242 }
243
244 void ExtensionBookmarkEventRouter::BookmarkNodeChildrenReordered(
245 BookmarkModel* model, const BookmarkNode* node) {
246 ListValue args;
247 args.Append(new StringValue(base::Int64ToString(node->id())));
248 int childCount = node->child_count();
249 ListValue* children = new ListValue();
250 for (int i = 0; i < childCount; ++i) {
251 const BookmarkNode* child = node->GetChild(i);
252 Value* child_id = new StringValue(base::Int64ToString(child->id()));
253 children->Append(child_id);
254 }
255 DictionaryValue* reorder_info = new DictionaryValue();
256 reorder_info->Set(keys::kChildIdsKey, children);
257 args.Append(reorder_info);
258
259 std::string json_args;
260 base::JSONWriter::Write(&args, false, &json_args);
261 DispatchEvent(model->profile(),
262 keys::kOnBookmarkChildrenReordered,
263 json_args);
264 }
265
266 void ExtensionBookmarkEventRouter::
267 BookmarkImportBeginning(BookmarkModel* model) {
268 ListValue args;
269 std::string json_args;
270 base::JSONWriter::Write(&args, false, &json_args);
271 DispatchEvent(model->profile(),
272 keys::kOnBookmarkImportBegan,
273 json_args);
274 }
275
276 void ExtensionBookmarkEventRouter::BookmarkImportEnding(BookmarkModel* model) {
277 ListValue args;
278 std::string json_args;
279 base::JSONWriter::Write(&args, false, &json_args);
280 DispatchEvent(model->profile(),
281 keys::kOnBookmarkImportEnded,
282 json_args);
283 }
284
285 bool GetBookmarksFunction::RunImpl() {
286 BookmarkModel* model = profile()->GetBookmarkModel();
287 scoped_ptr<ListValue> json(new ListValue());
288 Value* arg0;
289 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &arg0));
290 if (arg0->IsType(Value::TYPE_LIST)) {
291 const ListValue* ids = static_cast<const ListValue*>(arg0);
292 size_t count = ids->GetSize();
293 EXTENSION_FUNCTION_VALIDATE(count > 0);
294 for (size_t i = 0; i < count; ++i) {
295 int64 id;
296 std::string id_string;
297 EXTENSION_FUNCTION_VALIDATE(ids->GetString(i, &id_string));
298 if (!GetBookmarkIdAsInt64(id_string, &id))
299 return false;
300 const BookmarkNode* node = model->GetNodeByID(id);
301 if (!node) {
302 error_ = keys::kNoNodeError;
303 return false;
304 } else {
305 extension_bookmark_helpers::AddNode(node, json.get(), false);
306 }
307 }
308 } else {
309 int64 id;
310 std::string id_string;
311 EXTENSION_FUNCTION_VALIDATE(arg0->GetAsString(&id_string));
312 if (!GetBookmarkIdAsInt64(id_string, &id))
313 return false;
314 const BookmarkNode* node = model->GetNodeByID(id);
315 if (!node) {
316 error_ = keys::kNoNodeError;
317 return false;
318 }
319 extension_bookmark_helpers::AddNode(node, json.get(), false);
320 }
321
322 result_.reset(json.release());
323 return true;
324 }
325
326 bool GetBookmarkChildrenFunction::RunImpl() {
327 BookmarkModel* model = profile()->GetBookmarkModel();
328 int64 id;
329 std::string id_string;
330 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &id_string));
331 if (!GetBookmarkIdAsInt64(id_string, &id))
332 return false;
333 scoped_ptr<ListValue> json(new ListValue());
334 const BookmarkNode* node = model->GetNodeByID(id);
335 if (!node) {
336 error_ = keys::kNoNodeError;
337 return false;
338 }
339 int child_count = node->child_count();
340 for (int i = 0; i < child_count; ++i) {
341 const BookmarkNode* child = node->GetChild(i);
342 extension_bookmark_helpers::AddNode(child, json.get(), false);
343 }
344
345 result_.reset(json.release());
346 return true;
347 }
348
349 bool GetBookmarkRecentFunction::RunImpl() {
350 int number_of_items;
351 EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &number_of_items));
352 if (number_of_items < 1)
353 return false;
354
355 BookmarkModel* model = profile()->GetBookmarkModel();
356 ListValue* json = new ListValue();
357 std::vector<const BookmarkNode*> nodes;
358 bookmark_utils::GetMostRecentlyAddedEntries(model, number_of_items, &nodes);
359 std::vector<const BookmarkNode*>::iterator i = nodes.begin();
360 for (; i != nodes.end(); ++i) {
361 const BookmarkNode* node = *i;
362 extension_bookmark_helpers::AddNode(node, json, false);
363 }
364 result_.reset(json);
365 return true;
366 }
367
368 bool GetBookmarkTreeFunction::RunImpl() {
369 BookmarkModel* model = profile()->GetBookmarkModel();
370 scoped_ptr<ListValue> json(new ListValue());
371 const BookmarkNode* node = model->root_node();
372 extension_bookmark_helpers::AddNode(node, json.get(), true);
373 result_.reset(json.release());
374 return true;
375 }
376
377 bool GetBookmarkSubTreeFunction::RunImpl() {
378 BookmarkModel* model = profile()->GetBookmarkModel();
379 scoped_ptr<ListValue> json(new ListValue());
380 Value* arg0;
381 EXTENSION_FUNCTION_VALIDATE(args_->Get(0, &arg0));
382 int64 id;
383 std::string id_string;
384 EXTENSION_FUNCTION_VALIDATE(arg0->GetAsString(&id_string));
385 if (!GetBookmarkIdAsInt64(id_string, &id))
386 return false;
387 const BookmarkNode* node = model->GetNodeByID(id);
388 if (!node) {
389 error_ = keys::kNoNodeError;
390 return false;
391 }
392 extension_bookmark_helpers::AddNode(node, json.get(), true);
393 result_.reset(json.release());
394 return true;
395 }
396
397 bool SearchBookmarksFunction::RunImpl() {
398 string16 query;
399 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &query));
400
401 BookmarkModel* model = profile()->GetBookmarkModel();
402 ListValue* json = new ListValue();
403 std::string lang = profile()->GetPrefs()->GetString(prefs::kAcceptLanguages);
404 std::vector<const BookmarkNode*> nodes;
405 bookmark_utils::GetBookmarksContainingText(model, query,
406 std::numeric_limits<int>::max(),
407 lang, &nodes);
408 std::vector<const BookmarkNode*>::iterator i = nodes.begin();
409 for (; i != nodes.end(); ++i) {
410 const BookmarkNode* node = *i;
411 extension_bookmark_helpers::AddNode(node, json, false);
412 }
413
414 result_.reset(json);
415 return true;
416 }
417
418 // static
419 bool RemoveBookmarkFunction::ExtractIds(const ListValue* args,
420 std::list<int64>* ids,
421 bool* invalid_id) {
422 std::string id_string;
423 if (!args->GetString(0, &id_string))
424 return false;
425 int64 id;
426 if (base::StringToInt64(id_string, &id))
427 ids->push_back(id);
428 else
429 *invalid_id = true;
430 return true;
431 }
432
433 bool RemoveBookmarkFunction::RunImpl() {
434 if (!EditBookmarksEnabled())
435 return false;
436 std::list<int64> ids;
437 bool invalid_id = false;
438 EXTENSION_FUNCTION_VALIDATE(ExtractIds(args_.get(), &ids, &invalid_id));
439 if (invalid_id) {
440 error_ = keys::kInvalidIdError;
441 return false;
442 }
443 bool recursive = false;
444 if (name() == RemoveTreeBookmarkFunction::function_name())
445 recursive = true;
446
447 BookmarkModel* model = profile()->GetBookmarkModel();
448 size_t count = ids.size();
449 EXTENSION_FUNCTION_VALIDATE(count > 0);
450 for (std::list<int64>::iterator it = ids.begin(); it != ids.end(); ++it) {
451 if (!extension_bookmark_helpers::RemoveNode(model, *it, recursive, &error_))
452 return false;
453 }
454 return true;
455 }
456
457 bool CreateBookmarkFunction::RunImpl() {
458 if (!EditBookmarksEnabled())
459 return false;
460 DictionaryValue* json;
461 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &json));
462 EXTENSION_FUNCTION_VALIDATE(json != NULL);
463
464 BookmarkModel* model = profile()->GetBookmarkModel();
465 int64 parentId;
466 if (!json->HasKey(keys::kParentIdKey)) {
467 // Optional, default to "other bookmarks".
468 parentId = model->other_node()->id();
469 } else {
470 std::string parentId_string;
471 EXTENSION_FUNCTION_VALIDATE(json->GetString(keys::kParentIdKey,
472 &parentId_string));
473 if (!GetBookmarkIdAsInt64(parentId_string, &parentId))
474 return false;
475 }
476 const BookmarkNode* parent = model->GetNodeByID(parentId);
477 if (!parent) {
478 error_ = keys::kNoParentError;
479 return false;
480 }
481 if (parent->is_root()) { // Can't create children of the root.
482 error_ = keys::kModifySpecialError;
483 return false;
484 }
485
486 int index;
487 if (!json->HasKey(keys::kIndexKey)) { // Optional (defaults to end).
488 index = parent->child_count();
489 } else {
490 EXTENSION_FUNCTION_VALIDATE(json->GetInteger(keys::kIndexKey, &index));
491 if (index > parent->child_count() || index < 0) {
492 error_ = keys::kInvalidIndexError;
493 return false;
494 }
495 }
496
497 string16 title;
498 json->GetString(keys::kTitleKey, &title); // Optional.
499 std::string url_string;
500 json->GetString(keys::kUrlKey, &url_string); // Optional.
501 GURL url(url_string);
502 if (!url.is_empty() && !url.is_valid()) {
503 error_ = keys::kInvalidUrlError;
504 return false;
505 }
506
507 const BookmarkNode* node;
508 if (url_string.length())
509 node = model->AddURL(parent, index, title, url);
510 else
511 node = model->AddFolder(parent, index, title);
512 DCHECK(node);
513 if (!node) {
514 error_ = keys::kNoNodeError;
515 return false;
516 }
517
518 DictionaryValue* ret =
519 extension_bookmark_helpers::GetNodeDictionary(node, false, false);
520 result_.reset(ret);
521
522 return true;
523 }
524
525 // static
526 bool MoveBookmarkFunction::ExtractIds(const ListValue* args,
527 std::list<int64>* ids,
528 bool* invalid_id) {
529 // For now, Move accepts ID parameters in the same way as an Update.
530 return UpdateBookmarkFunction::ExtractIds(args, ids, invalid_id);
531 }
532
533 bool MoveBookmarkFunction::RunImpl() {
534 if (!EditBookmarksEnabled())
535 return false;
536 std::list<int64> ids;
537 bool invalid_id = false;
538 EXTENSION_FUNCTION_VALIDATE(ExtractIds(args_.get(), &ids, &invalid_id));
539 if (invalid_id) {
540 error_ = keys::kInvalidIdError;
541 return false;
542 }
543 EXTENSION_FUNCTION_VALIDATE(ids.size() == 1);
544
545 DictionaryValue* destination;
546 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &destination));
547
548 BookmarkModel* model = profile()->GetBookmarkModel();
549 const BookmarkNode* node = model->GetNodeByID(ids.front());
550 if (!node) {
551 error_ = keys::kNoNodeError;
552 return false;
553 }
554 if (model->is_permanent_node(node)) {
555 error_ = keys::kModifySpecialError;
556 return false;
557 }
558
559 const BookmarkNode* parent = NULL;
560 if (!destination->HasKey(keys::kParentIdKey)) {
561 // Optional, defaults to current parent.
562 parent = node->parent();
563 } else {
564 std::string parentId_string;
565 EXTENSION_FUNCTION_VALIDATE(destination->GetString(keys::kParentIdKey,
566 &parentId_string));
567 int64 parentId;
568 if (!GetBookmarkIdAsInt64(parentId_string, &parentId))
569 return false;
570
571 parent = model->GetNodeByID(parentId);
572 }
573 if (!parent) {
574 error_ = keys::kNoParentError;
575 // TODO(erikkay) return an error message.
576 return false;
577 }
578 if (parent == model->root_node()) {
579 error_ = keys::kModifySpecialError;
580 return false;
581 }
582
583 int index;
584 if (destination->HasKey(keys::kIndexKey)) { // Optional (defaults to end).
585 EXTENSION_FUNCTION_VALIDATE(destination->GetInteger(keys::kIndexKey,
586 &index));
587 if (index > parent->child_count() || index < 0) {
588 error_ = keys::kInvalidIndexError;
589 return false;
590 }
591 } else {
592 index = parent->child_count();
593 }
594
595 model->Move(node, parent, index);
596
597 DictionaryValue* ret =
598 extension_bookmark_helpers::GetNodeDictionary(node, false, false);
599 result_.reset(ret);
600
601 return true;
602 }
603
604 // static
605 bool UpdateBookmarkFunction::ExtractIds(const ListValue* args,
606 std::list<int64>* ids,
607 bool* invalid_id) {
608 // For now, Update accepts ID parameters in the same way as an Remove.
609 return RemoveBookmarkFunction::ExtractIds(args, ids, invalid_id);
610 }
611
612 bool UpdateBookmarkFunction::RunImpl() {
613 if (!EditBookmarksEnabled())
614 return false;
615 std::list<int64> ids;
616 bool invalid_id = false;
617 EXTENSION_FUNCTION_VALIDATE(ExtractIds(args_.get(), &ids, &invalid_id));
618 if (invalid_id) {
619 error_ = keys::kInvalidIdError;
620 return false;
621 }
622 EXTENSION_FUNCTION_VALIDATE(ids.size() == 1);
623
624 DictionaryValue* updates;
625 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &updates));
626
627 // Optional but we need to distinguish non present from an empty title.
628 string16 title;
629 const bool has_title = updates->GetString(keys::kTitleKey, &title);
630
631 // Optional.
632 std::string url_string;
633 updates->GetString(keys::kUrlKey, &url_string);
634 GURL url(url_string);
635 if (!url_string.empty() && !url.is_valid()) {
636 error_ = keys::kInvalidUrlError;
637 return false;
638 }
639
640 BookmarkModel* model = profile()->GetBookmarkModel();
641 const BookmarkNode* node = model->GetNodeByID(ids.front());
642 if (!node) {
643 error_ = keys::kNoNodeError;
644 return false;
645 }
646 if (model->is_permanent_node(node)) {
647 error_ = keys::kModifySpecialError;
648 return false;
649 }
650 if (has_title)
651 model->SetTitle(node, title);
652 if (!url.is_empty())
653 model->SetURL(node, url);
654
655 DictionaryValue* ret =
656 extension_bookmark_helpers::GetNodeDictionary(node, false, false);
657 result_.reset(ret);
658
659 return true;
660 }
661
662 // Mapper superclass for BookmarkFunctions.
663 template <typename BucketIdType>
664 class BookmarkBucketMapper : public BucketMapper {
665 public:
666 virtual ~BookmarkBucketMapper() { STLDeleteValues(&buckets_); }
667 protected:
668 Bucket* GetBucket(const BucketIdType& id) {
669 Bucket* b = buckets_[id];
670 if (b == NULL) {
671 b = new Bucket();
672 buckets_[id] = b;
673 }
674 return b;
675 }
676 private:
677 std::map<BucketIdType, Bucket*> buckets_;
678 };
679
680 // Mapper for 'bookmarks.create'. Maps "same input to bookmarks.create" to a
681 // unique bucket.
682 class CreateBookmarkBucketMapper : public BookmarkBucketMapper<std::string> {
683 public:
684 explicit CreateBookmarkBucketMapper(Profile* profile) : profile_(profile) {}
685 // TODO(tim): This should share code with CreateBookmarkFunction::RunImpl,
686 // but I can't figure out a good way to do that with all the macros.
687 virtual void GetBucketsForArgs(const ListValue* args, BucketList* buckets) {
688 DictionaryValue* json;
689 if (!args->GetDictionary(0, &json))
690 return;
691
692 std::string parent_id;
693 if (json->HasKey(keys::kParentIdKey)) {
694 if (!json->GetString(keys::kParentIdKey, &parent_id))
695 return;
696 }
697 BookmarkModel* model = profile_->GetBookmarkModel();
698
699 int64 parent_id_int64;
700 base::StringToInt64(parent_id, &parent_id_int64);
701 const BookmarkNode* parent = model->GetNodeByID(parent_id_int64);
702 if (!parent)
703 return;
704
705 std::string bucket_id = UTF16ToUTF8(parent->GetTitle());
706 std::string title;
707 json->GetString(keys::kTitleKey, &title);
708 std::string url_string;
709 json->GetString(keys::kUrlKey, &url_string);
710
711 bucket_id += title;
712 bucket_id += url_string;
713 // 20 bytes (SHA1 hash length) is very likely less than most of the
714 // |bucket_id| strings we construct here, so we hash it to save space.
715 buckets->push_back(GetBucket(base::SHA1HashString(bucket_id)));
716 }
717 private:
718 Profile* profile_;
719 };
720
721 // Mapper for 'bookmarks.remove'.
722 class RemoveBookmarksBucketMapper : public BookmarkBucketMapper<std::string> {
723 public:
724 explicit RemoveBookmarksBucketMapper(Profile* profile) : profile_(profile) {}
725 virtual void GetBucketsForArgs(const ListValue* args, BucketList* buckets) {
726 typedef std::list<int64> IdList;
727 IdList ids;
728 bool invalid_id = false;
729 if (!RemoveBookmarkFunction::ExtractIds(args, &ids, &invalid_id) ||
730 invalid_id) {
731 return;
732 }
733
734 for (IdList::iterator it = ids.begin(); it != ids.end(); ++it) {
735 BookmarkModel* model = profile_->GetBookmarkModel();
736 const BookmarkNode* node = model->GetNodeByID(*it);
737 if (!node || node->is_root())
738 return;
739
740 std::string bucket_id;
741 bucket_id += UTF16ToUTF8(node->parent()->GetTitle());
742 bucket_id += UTF16ToUTF8(node->GetTitle());
743 bucket_id += node->url().spec();
744 buckets->push_back(GetBucket(base::SHA1HashString(bucket_id)));
745 }
746 }
747 private:
748 Profile* profile_;
749 };
750
751 // Mapper for any bookmark function accepting bookmark IDs as parameters, where
752 // a distinct ID corresponds to a single item in terms of quota limiting. This
753 // is inappropriate for bookmarks.remove, for example, since repeated removals
754 // of the same item will actually have a different ID each time.
755 template <class FunctionType>
756 class BookmarkIdMapper : public BookmarkBucketMapper<int64> {
757 public:
758 typedef std::list<int64> IdList;
759 virtual void GetBucketsForArgs(const ListValue* args, BucketList* buckets) {
760 IdList ids;
761 bool invalid_id = false;
762 if (!FunctionType::ExtractIds(args, &ids, &invalid_id) || invalid_id)
763 return;
764 for (IdList::iterator it = ids.begin(); it != ids.end(); ++it)
765 buckets->push_back(GetBucket(*it));
766 }
767 };
768
769 // Builds heuristics for all BookmarkFunctions using specialized BucketMappers.
770 class BookmarksQuotaLimitFactory {
771 public:
772 // For id-based bookmark functions.
773 template <class FunctionType>
774 static void Build(QuotaLimitHeuristics* heuristics) {
775 BuildWithMappers(heuristics, new BookmarkIdMapper<FunctionType>(),
776 new BookmarkIdMapper<FunctionType>());
777 }
778
779 // For bookmarks.create.
780 static void BuildForCreate(QuotaLimitHeuristics* heuristics,
781 Profile* profile) {
782 BuildWithMappers(heuristics, new CreateBookmarkBucketMapper(profile),
783 new CreateBookmarkBucketMapper(profile));
784 }
785
786 // For bookmarks.remove.
787 static void BuildForRemove(QuotaLimitHeuristics* heuristics,
788 Profile* profile) {
789 BuildWithMappers(heuristics, new RemoveBookmarksBucketMapper(profile),
790 new RemoveBookmarksBucketMapper(profile));
791 }
792
793 private:
794 static void BuildWithMappers(QuotaLimitHeuristics* heuristics,
795 BucketMapper* short_mapper, BucketMapper* long_mapper) {
796 TimedLimit* timed = new TimedLimit(kLongLimitConfig, long_mapper);
797 // A max of two operations per minute, sustained over 10 minutes.
798 SustainedLimit* sustained = new SustainedLimit(TimeDelta::FromMinutes(10),
799 kShortLimitConfig, short_mapper);
800 heuristics->push_back(timed);
801 heuristics->push_back(sustained);
802 }
803
804 // The quota configurations used for all BookmarkFunctions.
805 static const Config kShortLimitConfig;
806 static const Config kLongLimitConfig;
807
808 DISALLOW_IMPLICIT_CONSTRUCTORS(BookmarksQuotaLimitFactory);
809 };
810
811 const Config BookmarksQuotaLimitFactory::kShortLimitConfig = {
812 2, // 2 tokens per interval.
813 TimeDelta::FromMinutes(1) // 1 minute long refill interval.
814 };
815
816 const Config BookmarksQuotaLimitFactory::kLongLimitConfig = {
817 100, // 100 tokens per interval.
818 TimeDelta::FromHours(1) // 1 hour long refill interval.
819 };
820
821 // And finally, building the individual heuristics for each function.
822 void RemoveBookmarkFunction::GetQuotaLimitHeuristics(
823 QuotaLimitHeuristics* heuristics) const {
824 BookmarksQuotaLimitFactory::BuildForRemove(heuristics, profile());
825 }
826
827 void MoveBookmarkFunction::GetQuotaLimitHeuristics(
828 QuotaLimitHeuristics* heuristics) const {
829 BookmarksQuotaLimitFactory::Build<MoveBookmarkFunction>(heuristics);
830 }
831
832 void UpdateBookmarkFunction::GetQuotaLimitHeuristics(
833 QuotaLimitHeuristics* heuristics) const {
834 BookmarksQuotaLimitFactory::Build<UpdateBookmarkFunction>(heuristics);
835 };
836
837 void CreateBookmarkFunction::GetQuotaLimitHeuristics(
838 QuotaLimitHeuristics* heuristics) const {
839 BookmarksQuotaLimitFactory::BuildForCreate(heuristics, profile());
840 }
841
842 BookmarksIOFunction::BookmarksIOFunction() {}
843
844 BookmarksIOFunction::~BookmarksIOFunction() {
845 // There may be pending file dialogs, we need to tell them that we've gone
846 // away so they don't try and call back to us.
847 if (select_file_dialog_.get())
848 select_file_dialog_->ListenerDestroyed();
849 }
850
851 void BookmarksIOFunction::SelectFile(SelectFileDialog::Type type) {
852 // GetDefaultFilepathForBookmarkExport() might have to touch the filesystem
853 // (stat or access, for example), so this requires a thread with IO allowed.
854 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
855 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
856 base::Bind(&BookmarksIOFunction::SelectFile, this, type));
857 return;
858 }
859
860 // Pre-populating the filename field in case this is a SELECT_SAVEAS_FILE
861 // dialog. If not, there is no filename field in the dialog box.
862 FilePath default_path;
863 if (type == SelectFileDialog::SELECT_SAVEAS_FILE)
864 default_path = GetDefaultFilepathForBookmarkExport();
865 else
866 DCHECK(type == SelectFileDialog::SELECT_OPEN_FILE);
867
868 // After getting the |default_path|, ask the UI to display the file dialog.
869 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
870 base::Bind(&BookmarksIOFunction::ShowSelectFileDialog, this,
871 type, default_path));
872 }
873
874 void BookmarksIOFunction::ShowSelectFileDialog(SelectFileDialog::Type type,
875 FilePath default_path) {
876 // Balanced in one of the three callbacks of SelectFileDialog:
877 // either FileSelectionCanceled, MultiFilesSelected, or FileSelected
878 AddRef();
879 select_file_dialog_ = SelectFileDialog::Create(this);
880 SelectFileDialog::FileTypeInfo file_type_info;
881 file_type_info.extensions.resize(1);
882 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("html"));
883
884 TabContents* tab_contents = dispatcher()->delegate()->
885 GetAssociatedTabContents();
886
887 // |tab_contents| can be NULL (for background pages), which is fine. In such
888 // a case if file-selection dialogs are forbidden by policy, we will not
889 // show an InfoBar, which is better than letting one appear out of the blue.
890 select_file_dialog_->SelectFile(type,
891 string16(),
892 default_path,
893 &file_type_info,
894 0,
895 FILE_PATH_LITERAL(""),
896 tab_contents,
897 NULL,
898 NULL);
899 }
900
901 void BookmarksIOFunction::FileSelectionCanceled(void* params) {
902 Release(); // Balanced in BookmarksIOFunction::SelectFile()
903 }
904
905 void BookmarksIOFunction::MultiFilesSelected(
906 const std::vector<FilePath>& files, void* params) {
907 Release(); // Balanced in BookmarsIOFunction::SelectFile()
908 NOTREACHED() << "Should not be able to select multiple files";
909 }
910
911 bool ImportBookmarksFunction::RunImpl() {
912 if (!EditBookmarksEnabled())
913 return false;
914 SelectFile(SelectFileDialog::SELECT_OPEN_FILE);
915 return true;
916 }
917
918 void ImportBookmarksFunction::FileSelected(const FilePath& path,
919 int index,
920 void* params) {
921 scoped_refptr<ImporterHost> importer_host(new ImporterHost);
922 importer::SourceProfile source_profile;
923 source_profile.importer_type = importer::TYPE_BOOKMARKS_FILE;
924 source_profile.source_path = path;
925 importer_host->StartImportSettings(source_profile,
926 profile(),
927 importer::FAVORITES,
928 new ProfileWriter(profile()),
929 true);
930 Release(); // Balanced in BookmarksIOFunction::SelectFile()
931 }
932
933 bool ExportBookmarksFunction::RunImpl() {
934 SelectFile(SelectFileDialog::SELECT_SAVEAS_FILE);
935 return true;
936 }
937
938 void ExportBookmarksFunction::FileSelected(const FilePath& path,
939 int index,
940 void* params) {
941 bookmark_html_writer::WriteBookmarks(profile(), path, NULL);
942 Release(); // Balanced in BookmarksIOFunction::SelectFile()
943 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698