- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / bookmark_manager_private / bookmark_manager_private_api.cc
1 // Copyright (c) 2012 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/api/bookmark_manager_private/bookmark_manager_private_api.h"
6
7 #include <vector>
8
9 #include "base/json/json_writer.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/values.h"
13 #include "chrome/browser/bookmarks/bookmark_model.h"
14 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
15 #include "chrome/browser/bookmarks/bookmark_node_data.h"
16 #include "chrome/browser/bookmarks/bookmark_stats.h"
17 #include "chrome/browser/bookmarks/bookmark_utils.h"
18 #include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_constants.h"
19 #include "chrome/browser/extensions/api/bookmarks/bookmark_api_constants.h"
20 #include "chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.h"
21 #include "chrome/browser/extensions/event_router.h"
22 #include "chrome/browser/extensions/extension_function_dispatcher.h"
23 #include "chrome/browser/extensions/extension_system.h"
24 #include "chrome/browser/extensions/extension_web_ui.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h"
27 #include "chrome/common/extensions/api/bookmark_manager_private.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/user_prefs/user_prefs.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/browser/web_contents.h"
32 #include "content/public/browser/web_contents_view.h"
33 #include "content/public/browser/web_ui.h"
34 #include "extensions/browser/view_type_utils.h"
35 #include "grit/generated_resources.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/webui/web_ui_util.h"
38
39 #if defined(OS_WIN)
40 #include "win8/util/win8_util.h"
41 #endif  // OS_WIN
42
43 namespace extensions {
44
45 namespace bookmark_keys = bookmark_api_constants;
46 namespace bookmark_manager_private = api::bookmark_manager_private;
47 namespace CanPaste = api::bookmark_manager_private::CanPaste;
48 namespace Copy = api::bookmark_manager_private::Copy;
49 namespace Cut = api::bookmark_manager_private::Cut;
50 namespace Drop = api::bookmark_manager_private::Drop;
51 namespace GetSubtree = api::bookmark_manager_private::GetSubtree;
52 namespace manager_keys = bookmark_manager_api_constants;
53 namespace Paste = api::bookmark_manager_private::Paste;
54 namespace RemoveTrees = api::bookmark_manager_private::RemoveTrees;
55 namespace SortChildren = api::bookmark_manager_private::SortChildren;
56 namespace StartDrag = api::bookmark_manager_private::StartDrag;
57
58 using content::WebContents;
59
60 namespace {
61
62 // Returns a single bookmark node from the argument ID.
63 // This returns NULL in case of failure.
64 const BookmarkNode* GetNodeFromString(
65     BookmarkModel* model, const std::string& id_string) {
66   int64 id;
67   if (!base::StringToInt64(id_string, &id))
68     return NULL;
69   return model->GetNodeByID(id);
70 }
71
72 // Gets a vector of bookmark nodes from the argument list of IDs.
73 // This returns false in the case of failure.
74 bool GetNodesFromVector(BookmarkModel* model,
75                         const std::vector<std::string>& id_strings,
76                         std::vector<const BookmarkNode*>* nodes) {
77
78   if (id_strings.empty())
79     return false;
80
81   for (size_t i = 0; i < id_strings.size(); ++i) {
82     const BookmarkNode* node = GetNodeFromString(model, id_strings[i]);
83     if (!node)
84       return false;
85     nodes->push_back(node);
86   }
87
88   return true;
89 }
90
91 // Recursively adds a node to a list. This is by used |BookmarkNodeDataToJSON|
92 // when the data comes from the current profile. In this case we have a
93 // BookmarkNode since we got the data from the current profile.
94 void AddNodeToList(base::ListValue* list, const BookmarkNode& node) {
95   base::DictionaryValue* dict = new base::DictionaryValue();
96
97   // Add id and parentId so we can associate the data with existing nodes on the
98   // client side.
99   std::string id_string = base::Int64ToString(node.id());
100   dict->SetString(bookmark_keys::kIdKey, id_string);
101
102   std::string parent_id_string = base::Int64ToString(node.parent()->id());
103   dict->SetString(bookmark_keys::kParentIdKey, parent_id_string);
104
105   if (node.is_url())
106     dict->SetString(bookmark_keys::kUrlKey, node.url().spec());
107
108   dict->SetString(bookmark_keys::kTitleKey, node.GetTitle());
109
110   base::ListValue* children = new base::ListValue();
111   for (int i = 0; i < node.child_count(); ++i)
112     AddNodeToList(children, *node.GetChild(i));
113   dict->Set(bookmark_keys::kChildrenKey, children);
114
115   list->Append(dict);
116 }
117
118 // Recursively adds an element to a list. This is used by
119 // |BookmarkNodeDataToJSON| when the data comes from a different profile. When
120 // the data comes from a different profile we do not have any IDs or parent IDs.
121 void AddElementToList(base::ListValue* list,
122                       const BookmarkNodeData::Element& element) {
123   base::DictionaryValue* dict = new base::DictionaryValue();
124
125   if (element.is_url)
126     dict->SetString(bookmark_keys::kUrlKey, element.url.spec());
127
128   dict->SetString(bookmark_keys::kTitleKey, element.title);
129
130   base::ListValue* children = new base::ListValue();
131   for (size_t i = 0; i < element.children.size(); ++i)
132     AddElementToList(children, element.children[i]);
133   dict->Set(bookmark_keys::kChildrenKey, children);
134
135   list->Append(dict);
136 }
137
138 // Builds the JSON structure based on the BookmarksDragData.
139 void BookmarkNodeDataToJSON(Profile* profile, const BookmarkNodeData& data,
140                             base::ListValue* args) {
141   bool same_profile = data.IsFromProfile(profile);
142   base::DictionaryValue* value = new base::DictionaryValue();
143   value->SetBoolean(manager_keys::kSameProfileKey, same_profile);
144
145   base::ListValue* list = new base::ListValue();
146   if (same_profile) {
147     std::vector<const BookmarkNode*> nodes = data.GetNodes(profile);
148     for (size_t i = 0; i < nodes.size(); ++i)
149       AddNodeToList(list, *nodes[i]);
150   } else {
151     // We do not have an node IDs when the data comes from a different profile.
152     std::vector<BookmarkNodeData::Element> elements = data.elements;
153     for (size_t i = 0; i < elements.size(); ++i)
154       AddElementToList(list, elements[i]);
155   }
156   value->Set(manager_keys::kElementsKey, list);
157
158   args->Append(value);
159 }
160
161 }  // namespace
162
163 BookmarkManagerPrivateEventRouter::BookmarkManagerPrivateEventRouter(
164     Profile* profile,
165     content::WebContents* web_contents)
166     : profile_(profile),
167       web_contents_(web_contents) {
168   BookmarkTabHelper* bookmark_tab_helper =
169       BookmarkTabHelper::FromWebContents(web_contents_);
170   bookmark_tab_helper->set_bookmark_drag_delegate(this);
171 }
172
173 BookmarkManagerPrivateEventRouter::~BookmarkManagerPrivateEventRouter() {
174   BookmarkTabHelper* bookmark_tab_helper =
175       BookmarkTabHelper::FromWebContents(web_contents_);
176   if (bookmark_tab_helper->bookmark_drag_delegate() == this)
177     bookmark_tab_helper->set_bookmark_drag_delegate(NULL);
178 }
179
180 void BookmarkManagerPrivateEventRouter::DispatchEvent(
181     const std::string& event_name,
182     scoped_ptr<base::ListValue> args) {
183   if (!ExtensionSystem::Get(profile_)->event_router())
184     return;
185
186   scoped_ptr<Event> event(new Event(event_name, args.Pass()));
187   ExtensionSystem::Get(profile_)->event_router()->BroadcastEvent(event.Pass());
188 }
189
190 void BookmarkManagerPrivateEventRouter::DispatchDragEvent(
191     const BookmarkNodeData& data,
192     const std::string& event_name) {
193   if (data.size() == 0)
194     return;
195
196   scoped_ptr<base::ListValue> args(new base::ListValue());
197   BookmarkNodeDataToJSON(profile_, data, args.get());
198   DispatchEvent(event_name, args.Pass());
199 }
200
201 void BookmarkManagerPrivateEventRouter::OnDragEnter(
202     const BookmarkNodeData& data) {
203   DispatchDragEvent(data, bookmark_manager_private::OnDragEnter::kEventName);
204 }
205
206 void BookmarkManagerPrivateEventRouter::OnDragOver(
207     const BookmarkNodeData& data) {
208   // Intentionally empty since these events happens too often and floods the
209   // message queue. We do not need this event for the bookmark manager anyway.
210 }
211
212 void BookmarkManagerPrivateEventRouter::OnDragLeave(
213     const BookmarkNodeData& data) {
214   DispatchDragEvent(data, bookmark_manager_private::OnDragLeave::kEventName);
215 }
216
217 void BookmarkManagerPrivateEventRouter::OnDrop(const BookmarkNodeData& data) {
218   DispatchDragEvent(data, bookmark_manager_private::OnDrop::kEventName);
219
220   // Make a copy that is owned by this instance.
221   ClearBookmarkNodeData();
222   bookmark_drag_data_ = data;
223 }
224
225 const BookmarkNodeData*
226 BookmarkManagerPrivateEventRouter::GetBookmarkNodeData() {
227   if (bookmark_drag_data_.is_valid())
228     return &bookmark_drag_data_;
229   return NULL;
230 }
231
232 void BookmarkManagerPrivateEventRouter::ClearBookmarkNodeData() {
233   bookmark_drag_data_.Clear();
234 }
235
236 bool ClipboardBookmarkManagerFunction::CopyOrCut(bool cut,
237     const std::vector<std::string>& id_list) {
238   BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
239   std::vector<const BookmarkNode*> nodes;
240   EXTENSION_FUNCTION_VALIDATE(GetNodesFromVector(model, id_list, &nodes));
241   bookmark_utils::CopyToClipboard(model, nodes, cut);
242   return true;
243 }
244
245 bool BookmarkManagerPrivateCopyFunction::RunImpl() {
246   scoped_ptr<Copy::Params> params(Copy::Params::Create(*args_));
247   EXTENSION_FUNCTION_VALIDATE(params);
248   return CopyOrCut(false, params->id_list);
249 }
250
251 bool BookmarkManagerPrivateCutFunction::RunImpl() {
252   if (!EditBookmarksEnabled())
253     return false;
254
255   scoped_ptr<Cut::Params> params(Cut::Params::Create(*args_));
256   EXTENSION_FUNCTION_VALIDATE(params);
257   return CopyOrCut(true, params->id_list);
258 }
259
260 bool BookmarkManagerPrivatePasteFunction::RunImpl() {
261   if (!EditBookmarksEnabled())
262     return false;
263
264   scoped_ptr<Paste::Params> params(Paste::Params::Create(*args_));
265   EXTENSION_FUNCTION_VALIDATE(params);
266   BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
267   const BookmarkNode* parent_node = GetNodeFromString(model, params->parent_id);
268   if (!parent_node) {
269     error_ = bookmark_keys::kNoParentError;
270     return false;
271   }
272   bool can_paste = bookmark_utils::CanPasteFromClipboard(parent_node);
273   if (!can_paste)
274     return false;
275
276   // We want to use the highest index of the selected nodes as a destination.
277   std::vector<const BookmarkNode*> nodes;
278   // No need to test return value, if we got an empty list, we insert at end.
279   if (params->selected_id_list)
280     GetNodesFromVector(model, *params->selected_id_list, &nodes);
281   int highest_index = -1;  // -1 means insert at end of list.
282   for (size_t i = 0; i < nodes.size(); ++i) {
283     // + 1 so that we insert after the selection.
284     int index = parent_node->GetIndexOf(nodes[i]) + 1;
285     if (index > highest_index)
286       highest_index = index;
287   }
288
289   bookmark_utils::PasteFromClipboard(model, parent_node, highest_index);
290   return true;
291 }
292
293 bool BookmarkManagerPrivateCanPasteFunction::RunImpl() {
294   if (!EditBookmarksEnabled())
295     return false;
296
297   scoped_ptr<CanPaste::Params> params(CanPaste::Params::Create(*args_));
298   EXTENSION_FUNCTION_VALIDATE(params);
299
300   BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
301   const BookmarkNode* parent_node = GetNodeFromString(model, params->parent_id);
302   if (!parent_node) {
303     error_ = bookmark_keys::kNoParentError;
304     return false;
305   }
306   bool can_paste = bookmark_utils::CanPasteFromClipboard(parent_node);
307   SetResult(new base::FundamentalValue(can_paste));
308   return true;
309 }
310
311 bool BookmarkManagerPrivateSortChildrenFunction::RunImpl() {
312   if (!EditBookmarksEnabled())
313     return false;
314
315   scoped_ptr<SortChildren::Params> params(SortChildren::Params::Create(*args_));
316   EXTENSION_FUNCTION_VALIDATE(params);
317
318   BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
319   const BookmarkNode* parent_node = GetNodeFromString(model, params->parent_id);
320   if (!parent_node) {
321     error_ = bookmark_keys::kNoParentError;
322     return false;
323   }
324   model->SortChildren(parent_node);
325   return true;
326 }
327
328 bool BookmarkManagerPrivateGetStringsFunction::RunImpl() {
329   base::DictionaryValue* localized_strings = new base::DictionaryValue();
330
331   localized_strings->SetString("title",
332       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_TITLE));
333   localized_strings->SetString("search_button",
334       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SEARCH_BUTTON));
335   localized_strings->SetString("organize_menu",
336       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_ORGANIZE_MENU));
337   localized_strings->SetString("show_in_folder",
338       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
339   localized_strings->SetString("sort",
340       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SORT));
341   localized_strings->SetString("import_menu",
342       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_IMPORT_MENU));
343   localized_strings->SetString("export_menu",
344       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_EXPORT_MENU));
345   localized_strings->SetString("rename_folder",
346       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_RENAME_FOLDER));
347   localized_strings->SetString("edit",
348       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_EDIT));
349   localized_strings->SetString("should_open_all",
350       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL));
351   localized_strings->SetString("open_incognito",
352       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_INCOGNITO));
353   localized_strings->SetString("open_in_new_tab",
354       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_IN_NEW_TAB));
355   localized_strings->SetString("open_in_new_window",
356       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_IN_NEW_WINDOW));
357   localized_strings->SetString("add_new_bookmark",
358       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
359   localized_strings->SetString("new_folder",
360       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_NEW_FOLDER));
361   localized_strings->SetString("open_all",
362       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_ALL));
363   localized_strings->SetString("open_all_new_window",
364       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
365   localized_strings->SetString("open_all_incognito",
366       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
367   localized_strings->SetString("remove",
368       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_REMOVE));
369   localized_strings->SetString("copy",
370       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_COPY));
371   localized_strings->SetString("cut",
372       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_CUT));
373   localized_strings->SetString("paste",
374       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PASTE));
375   localized_strings->SetString("delete",
376       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_DELETE));
377   localized_strings->SetString("undo_delete",
378       l10n_util::GetStringUTF16(IDS_UNDO_DELETE));
379   localized_strings->SetString("new_folder_name",
380       l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_NEW_FOLDER_NAME));
381   localized_strings->SetString("name_input_placeholder",
382       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_NAME_INPUT_PLACE_HOLDER));
383   localized_strings->SetString("url_input_placeholder",
384       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_URL_INPUT_PLACE_HOLDER));
385   localized_strings->SetString("invalid_url",
386       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_INVALID_URL));
387   localized_strings->SetString("recent",
388       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_RECENT));
389   localized_strings->SetString("search",
390       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SEARCH));
391   localized_strings->SetString("save",
392       l10n_util::GetStringUTF16(IDS_SAVE));
393   localized_strings->SetString("cancel",
394       l10n_util::GetStringUTF16(IDS_CANCEL));
395
396   webui::SetFontAndTextDirection(localized_strings);
397
398   SetResult(localized_strings);
399
400   // This is needed because unlike the rest of these functions, this class
401   // inherits from AsyncFunction directly, rather than BookmarkFunction.
402   SendResponse(true);
403
404   return true;
405 }
406
407 bool BookmarkManagerPrivateStartDragFunction::RunImpl() {
408   if (!EditBookmarksEnabled())
409     return false;
410
411   scoped_ptr<StartDrag::Params> params(StartDrag::Params::Create(*args_));
412   EXTENSION_FUNCTION_VALIDATE(params);
413
414   BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
415   std::vector<const BookmarkNode*> nodes;
416   EXTENSION_FUNCTION_VALIDATE(
417       GetNodesFromVector(model, params->id_list, &nodes));
418
419   WebContents* web_contents =
420       WebContents::FromRenderViewHost(render_view_host_);
421   if (GetViewType(web_contents) == VIEW_TYPE_TAB_CONTENTS) {
422     WebContents* web_contents =
423         dispatcher()->delegate()->GetAssociatedWebContents();
424     CHECK(web_contents);
425     chrome::DragBookmarks(
426         GetProfile(), nodes, web_contents->GetView()->GetNativeView());
427
428     return true;
429   } else {
430     NOTREACHED();
431     return false;
432   }
433 }
434
435 bool BookmarkManagerPrivateDropFunction::RunImpl() {
436   if (!EditBookmarksEnabled())
437     return false;
438
439   scoped_ptr<Drop::Params> params(Drop::Params::Create(*args_));
440   EXTENSION_FUNCTION_VALIDATE(params);
441
442   BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
443
444   const BookmarkNode* drop_parent = GetNodeFromString(model, params->parent_id);
445   if (!drop_parent) {
446     error_ = bookmark_keys::kNoParentError;
447     return false;
448   }
449
450   int drop_index;
451   if (params->index)
452     drop_index = *params->index;
453   else
454     drop_index = drop_parent->child_count();
455
456   WebContents* web_contents =
457       WebContents::FromRenderViewHost(render_view_host_);
458   if (GetViewType(web_contents) == VIEW_TYPE_TAB_CONTENTS) {
459     WebContents* web_contents =
460         dispatcher()->delegate()->GetAssociatedWebContents();
461     CHECK(web_contents);
462     ExtensionWebUI* web_ui =
463         static_cast<ExtensionWebUI*>(web_contents->GetWebUI()->GetController());
464     CHECK(web_ui);
465     BookmarkManagerPrivateEventRouter* router =
466         web_ui->bookmark_manager_private_event_router();
467
468     DCHECK(router);
469     const BookmarkNodeData* drag_data = router->GetBookmarkNodeData();
470     if (drag_data == NULL) {
471       NOTREACHED() <<"Somehow we're dropping null bookmark data";
472       return false;
473     }
474     chrome::DropBookmarks(GetProfile(), *drag_data, drop_parent, drop_index);
475
476     router->ClearBookmarkNodeData();
477     return true;
478   } else {
479     NOTREACHED();
480     return false;
481   }
482 }
483
484 bool BookmarkManagerPrivateGetSubtreeFunction::RunImpl() {
485   scoped_ptr<GetSubtree::Params> params(GetSubtree::Params::Create(*args_));
486   EXTENSION_FUNCTION_VALIDATE(params);
487
488   BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
489   const BookmarkNode* node = NULL;
490
491   if (params->id == "") {
492     node = model->root_node();
493   } else {
494     int64 id;
495     if (!base::StringToInt64(params->id, &id)) {
496       error_ = bookmark_keys::kInvalidIdError;
497       return false;
498     }
499     node = model->GetNodeByID(id);
500   }
501
502   if (!node) {
503     error_ = bookmark_keys::kNoNodeError;
504     return false;
505   }
506
507   scoped_ptr<base::ListValue> json(new base::ListValue());
508   if (params->folders_only)
509     bookmark_api_helpers::AddNodeFoldersOnly(node, json.get(), true);
510   else
511     bookmark_api_helpers::AddNode(node, json.get(), true);
512   SetResult(json.release());
513   return true;
514 }
515
516 bool BookmarkManagerPrivateCanEditFunction::RunImpl() {
517   PrefService* prefs = user_prefs::UserPrefs::Get(GetProfile());
518   SetResult(new base::FundamentalValue(
519       prefs->GetBoolean(prefs::kEditBookmarksEnabled)));
520   return true;
521 }
522
523 bool BookmarkManagerPrivateRecordLaunchFunction::RunImpl() {
524   RecordBookmarkLaunch(NULL, BOOKMARK_LAUNCH_LOCATION_MANAGER);
525   return true;
526 }
527
528 bool BookmarkManagerPrivateCanOpenNewWindowsFunction::RunImpl() {
529   bool can_open_new_windows = true;
530
531 #if defined(OS_WIN)
532   if (win8::IsSingleWindowMetroMode())
533     can_open_new_windows = false;
534 #endif  // OS_WIN
535
536   SetResult(new base::FundamentalValue(can_open_new_windows));
537   return true;
538 }
539
540 bool BookmarkManagerPrivateRemoveTreesFunction::RunImpl() {
541   scoped_ptr<RemoveTrees::Params> params(RemoveTrees::Params::Create(*args_));
542   EXTENSION_FUNCTION_VALIDATE(params);
543
544   BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
545   int64 id;
546   for (size_t i = 0; i < params->id_list.size(); ++i) {
547     if (!base::StringToInt64(params->id_list[i], &id)) {
548       error_ = bookmark_api_constants::kInvalidIdError;
549       return false;
550     }
551     if (!bookmark_api_helpers::RemoveNode(model, id, true, &error_))
552       return false;
553   }
554
555   return true;
556 }
557
558 }  // namespace extensions