15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/lazy_instance.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/linked_ptr.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/prefs/pref_service.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/strings/string_number_conversions.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/utf_string_conversions.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/bookmarks/bookmark_model_factory.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/bookmarks/bookmark_stats.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/bookmarks/chrome_bookmark_client.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/api/bookmarks/bookmark_api_constants.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_web_ui.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/undo/bookmark_undo_service.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/undo/bookmark_undo_service_factory.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/api/bookmark_manager_private.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/pref_names.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/grit/generated_resources.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "components/bookmarks/browser/bookmark_model.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "components/bookmarks/browser/bookmark_node_data.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "components/bookmarks/browser/bookmark_utils.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "components/bookmarks/browser/scoped_group_bookmark_actions.h"
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "components/user_prefs/user_prefs.h"
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h"
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
35eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "content/public/browser/web_ui.h"
36eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "extensions/browser/extension_function_dispatcher.h"
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/browser/view_type_utils.h"
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/dragdrop/drag_drop_types.h"
3958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
4058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "ui/base/webui/web_ui_util.h"
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using bookmarks::BookmarkNodeData;
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace extensions {
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace bookmark_keys = bookmark_api_constants;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace bookmark_manager_private = api::bookmark_manager_private;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace CanPaste = api::bookmark_manager_private::CanPaste;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace Copy = api::bookmark_manager_private::Copy;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace CreateWithMetaInfo =
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    api::bookmark_manager_private::CreateWithMetaInfo;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace Cut = api::bookmark_manager_private::Cut;
53namespace Drop = api::bookmark_manager_private::Drop;
54namespace GetSubtree = api::bookmark_manager_private::GetSubtree;
55namespace GetMetaInfo = api::bookmark_manager_private::GetMetaInfo;
56namespace Paste = api::bookmark_manager_private::Paste;
57namespace RedoInfo = api::bookmark_manager_private::GetRedoInfo;
58namespace RemoveTrees = api::bookmark_manager_private::RemoveTrees;
59namespace SetMetaInfo = api::bookmark_manager_private::SetMetaInfo;
60namespace SortChildren = api::bookmark_manager_private::SortChildren;
61namespace StartDrag = api::bookmark_manager_private::StartDrag;
62namespace UndoInfo = api::bookmark_manager_private::GetUndoInfo;
63namespace UpdateMetaInfo = api::bookmark_manager_private::UpdateMetaInfo;
64
65using content::WebContents;
66
67namespace {
68
69// Returns a single bookmark node from the argument ID.
70// This returns NULL in case of failure.
71const BookmarkNode* GetNodeFromString(BookmarkModel* model,
72                                      const std::string& id_string) {
73  int64 id;
74  if (!base::StringToInt64(id_string, &id))
75    return NULL;
76  return bookmarks::GetBookmarkNodeByID(model, id);
77}
78
79// Gets a vector of bookmark nodes from the argument list of IDs.
80// This returns false in the case of failure.
81bool GetNodesFromVector(BookmarkModel* model,
82                        const std::vector<std::string>& id_strings,
83                        std::vector<const BookmarkNode*>* nodes) {
84  if (id_strings.empty())
85    return false;
86
87  for (size_t i = 0; i < id_strings.size(); ++i) {
88    const BookmarkNode* node = GetNodeFromString(model, id_strings[i]);
89    if (!node)
90      return false;
91    nodes->push_back(node);
92  }
93
94  return true;
95}
96
97// Recursively create a bookmark_manager_private::BookmarkNodeDataElement from
98// a bookmark node. This is by used |BookmarkNodeDataToJSON| when the data comes
99// from the current profile. In this case we have a BookmarkNode since we got
100// the data from the current profile.
101linked_ptr<bookmark_manager_private::BookmarkNodeDataElement>
102CreateNodeDataElementFromBookmarkNode(const BookmarkNode& node) {
103  linked_ptr<bookmark_manager_private::BookmarkNodeDataElement> element(
104      new bookmark_manager_private::BookmarkNodeDataElement);
105  // Add id and parentId so we can associate the data with existing nodes on the
106  // client side.
107  element->id.reset(new std::string(base::Int64ToString(node.id())));
108  element->parent_id.reset(
109      new std::string(base::Int64ToString(node.parent()->id())));
110
111  if (node.is_url())
112    element->url.reset(new std::string(node.url().spec()));
113
114  element->title = base::UTF16ToUTF8(node.GetTitle());
115  for (int i = 0; i < node.child_count(); ++i) {
116    element->children.push_back(
117        CreateNodeDataElementFromBookmarkNode(*node.GetChild(i)));
118  }
119
120  return element;
121}
122
123// Recursively create a bookmark_manager_private::BookmarkNodeDataElement from
124// a BookmarkNodeData::Element. This is used by |BookmarkNodeDataToJSON| when
125// the data comes from a different profile. When the data comes from a different
126// profile we do not have any IDs or parent IDs.
127linked_ptr<bookmark_manager_private::BookmarkNodeDataElement>
128CreateApiNodeDataElement(const BookmarkNodeData::Element& element) {
129  linked_ptr<bookmark_manager_private::BookmarkNodeDataElement> node_element(
130      new bookmark_manager_private::BookmarkNodeDataElement);
131
132  if (element.is_url)
133    node_element->url.reset(new std::string(element.url.spec()));
134  node_element->title = base::UTF16ToUTF8(element.title);
135  for (size_t i = 0; i < element.children.size(); ++i) {
136    node_element->children.push_back(
137        CreateApiNodeDataElement(element.children[i]));
138  }
139
140  return node_element;
141}
142
143// Creates a bookmark_manager_private::BookmarkNodeData from a BookmarkNodeData.
144scoped_ptr<bookmark_manager_private::BookmarkNodeData>
145CreateApiBookmarkNodeData(Profile* profile, const BookmarkNodeData& data) {
146  const base::FilePath& profile_path = profile->GetPath();
147
148  scoped_ptr<bookmark_manager_private::BookmarkNodeData> node_data(
149      new bookmark_manager_private::BookmarkNodeData);
150  node_data->same_profile = data.IsFromProfilePath(profile_path);
151
152  if (node_data->same_profile) {
153    std::vector<const BookmarkNode*> nodes = data.GetNodes(
154        BookmarkModelFactory::GetForProfile(profile), profile_path);
155    for (size_t i = 0; i < nodes.size(); ++i) {
156      node_data->elements.push_back(
157          CreateNodeDataElementFromBookmarkNode(*nodes[i]));
158    }
159  } else {
160    // We do not have a node IDs when the data comes from a different profile.
161    std::vector<BookmarkNodeData::Element> elements = data.elements;
162    for (size_t i = 0; i < elements.size(); ++i)
163      node_data->elements.push_back(CreateApiNodeDataElement(elements[i]));
164  }
165  return node_data.Pass();
166}
167
168}  // namespace
169
170BookmarkManagerPrivateEventRouter::BookmarkManagerPrivateEventRouter(
171    content::BrowserContext* browser_context,
172    BookmarkModel* bookmark_model)
173    : browser_context_(browser_context), bookmark_model_(bookmark_model) {
174  bookmark_model_->AddObserver(this);
175}
176
177BookmarkManagerPrivateEventRouter::~BookmarkManagerPrivateEventRouter() {
178  if (bookmark_model_)
179    bookmark_model_->RemoveObserver(this);
180}
181
182void BookmarkManagerPrivateEventRouter::DispatchEvent(
183    const std::string& event_name,
184    scoped_ptr<base::ListValue> event_args) {
185  extensions::EventRouter::Get(browser_context_)->BroadcastEvent(
186      make_scoped_ptr(new extensions::Event(event_name, event_args.Pass())));
187}
188
189void BookmarkManagerPrivateEventRouter::BookmarkModelChanged() {}
190
191void BookmarkManagerPrivateEventRouter::BookmarkModelBeingDeleted(
192    BookmarkModel* model) {
193  bookmark_model_ = NULL;
194}
195
196void BookmarkManagerPrivateEventRouter::OnWillChangeBookmarkMetaInfo(
197    BookmarkModel* model,
198    const BookmarkNode* node) {
199  DCHECK(prev_meta_info_.empty());
200  if (node->GetMetaInfoMap())
201    prev_meta_info_ = *node->GetMetaInfoMap();
202}
203
204void BookmarkManagerPrivateEventRouter::BookmarkMetaInfoChanged(
205    BookmarkModel* model,
206    const BookmarkNode* node) {
207  const BookmarkNode::MetaInfoMap* new_meta_info = node->GetMetaInfoMap();
208  bookmark_manager_private::MetaInfoFields changes;
209
210  // Identify changed/removed fields:
211  for (BookmarkNode::MetaInfoMap::const_iterator it = prev_meta_info_.begin();
212       it != prev_meta_info_.end();
213       ++it) {
214    if (!new_meta_info) {
215      changes.additional_properties[it->first] = "";
216    } else {
217      BookmarkNode::MetaInfoMap::const_iterator new_meta_field =
218          new_meta_info->find(it->first);
219      if (new_meta_field == new_meta_info->end()) {
220        changes.additional_properties[it->first] = "";
221      } else if (it->second != new_meta_field->second) {
222        changes.additional_properties[it->first] = new_meta_field->second;
223      }
224    }
225  }
226
227  // Identify added fields:
228  if (new_meta_info) {
229    for (BookmarkNode::MetaInfoMap::const_iterator it = new_meta_info->begin();
230         it != new_meta_info->end();
231         ++it) {
232      BookmarkNode::MetaInfoMap::const_iterator prev_meta_field =
233          prev_meta_info_.find(it->first);
234      if (prev_meta_field == prev_meta_info_.end())
235        changes.additional_properties[it->first] = it->second;
236    }
237  }
238
239  prev_meta_info_.clear();
240  DispatchEvent(bookmark_manager_private::OnMetaInfoChanged::kEventName,
241                bookmark_manager_private::OnMetaInfoChanged::Create(
242                    base::Int64ToString(node->id()), changes));
243}
244
245BookmarkManagerPrivateAPI::BookmarkManagerPrivateAPI(
246    content::BrowserContext* browser_context)
247    : browser_context_(browser_context) {
248  EventRouter* event_router = EventRouter::Get(browser_context);
249  event_router->RegisterObserver(
250      this, bookmark_manager_private::OnMetaInfoChanged::kEventName);
251}
252
253BookmarkManagerPrivateAPI::~BookmarkManagerPrivateAPI() {}
254
255void BookmarkManagerPrivateAPI::Shutdown() {
256  EventRouter::Get(browser_context_)->UnregisterObserver(this);
257}
258
259static base::LazyInstance<
260    BrowserContextKeyedAPIFactory<BookmarkManagerPrivateAPI> > g_factory =
261    LAZY_INSTANCE_INITIALIZER;
262
263// static
264BrowserContextKeyedAPIFactory<BookmarkManagerPrivateAPI>*
265BookmarkManagerPrivateAPI::GetFactoryInstance() {
266  return g_factory.Pointer();
267}
268
269void BookmarkManagerPrivateAPI::OnListenerAdded(
270    const EventListenerInfo& details) {
271  EventRouter::Get(browser_context_)->UnregisterObserver(this);
272  event_router_.reset(new BookmarkManagerPrivateEventRouter(
273      browser_context_,
274      BookmarkModelFactory::GetForProfile(
275          Profile::FromBrowserContext(browser_context_))));
276}
277
278BookmarkManagerPrivateDragEventRouter::BookmarkManagerPrivateDragEventRouter(
279    Profile* profile,
280    content::WebContents* web_contents)
281    : profile_(profile), web_contents_(web_contents) {
282  BookmarkTabHelper* bookmark_tab_helper =
283      BookmarkTabHelper::FromWebContents(web_contents_);
284  bookmark_tab_helper->set_bookmark_drag_delegate(this);
285}
286
287BookmarkManagerPrivateDragEventRouter::
288    ~BookmarkManagerPrivateDragEventRouter() {
289  BookmarkTabHelper* bookmark_tab_helper =
290      BookmarkTabHelper::FromWebContents(web_contents_);
291  if (bookmark_tab_helper->bookmark_drag_delegate() == this)
292    bookmark_tab_helper->set_bookmark_drag_delegate(NULL);
293}
294
295void BookmarkManagerPrivateDragEventRouter::DispatchEvent(
296    const std::string& event_name,
297    scoped_ptr<base::ListValue> args) {
298  EventRouter* event_router = EventRouter::Get(profile_);
299  if (!event_router)
300    return;
301
302  scoped_ptr<Event> event(new Event(event_name, args.Pass()));
303  event_router->BroadcastEvent(event.Pass());
304}
305
306void BookmarkManagerPrivateDragEventRouter::OnDragEnter(
307    const BookmarkNodeData& data) {
308  if (data.size() == 0)
309    return;
310  DispatchEvent(bookmark_manager_private::OnDragEnter::kEventName,
311                bookmark_manager_private::OnDragEnter::Create(
312                    *CreateApiBookmarkNodeData(profile_, data)));
313}
314
315void BookmarkManagerPrivateDragEventRouter::OnDragOver(
316    const BookmarkNodeData& data) {
317  // Intentionally empty since these events happens too often and floods the
318  // message queue. We do not need this event for the bookmark manager anyway.
319}
320
321void BookmarkManagerPrivateDragEventRouter::OnDragLeave(
322    const BookmarkNodeData& data) {
323  if (data.size() == 0)
324    return;
325  DispatchEvent(bookmark_manager_private::OnDragLeave::kEventName,
326                bookmark_manager_private::OnDragLeave::Create(
327                    *CreateApiBookmarkNodeData(profile_, data)));
328}
329
330void BookmarkManagerPrivateDragEventRouter::OnDrop(
331    const BookmarkNodeData& data) {
332  if (data.size() == 0)
333    return;
334  DispatchEvent(bookmark_manager_private::OnDrop::kEventName,
335                bookmark_manager_private::OnDrop::Create(
336                    *CreateApiBookmarkNodeData(profile_, data)));
337
338  // Make a copy that is owned by this instance.
339  ClearBookmarkNodeData();
340  bookmark_drag_data_ = data;
341}
342
343const BookmarkNodeData*
344BookmarkManagerPrivateDragEventRouter::GetBookmarkNodeData() {
345  if (bookmark_drag_data_.is_valid())
346    return &bookmark_drag_data_;
347  return NULL;
348}
349
350void BookmarkManagerPrivateDragEventRouter::ClearBookmarkNodeData() {
351  bookmark_drag_data_.Clear();
352}
353
354bool ClipboardBookmarkManagerFunction::CopyOrCut(bool cut,
355    const std::vector<std::string>& id_list) {
356  BookmarkModel* model = GetBookmarkModel();
357  ChromeBookmarkClient* client = GetChromeBookmarkClient();
358  std::vector<const BookmarkNode*> nodes;
359  EXTENSION_FUNCTION_VALIDATE(GetNodesFromVector(model, id_list, &nodes));
360  if (cut && client->HasDescendantsOfManagedNode(nodes)) {
361    error_ = bookmark_keys::kModifyManagedError;
362    return false;
363  }
364  bookmarks::CopyToClipboard(model, nodes, cut);
365  return true;
366}
367
368bool BookmarkManagerPrivateCopyFunction::RunOnReady() {
369  scoped_ptr<Copy::Params> params(Copy::Params::Create(*args_));
370  EXTENSION_FUNCTION_VALIDATE(params);
371  return CopyOrCut(false, params->id_list);
372}
373
374bool BookmarkManagerPrivateCutFunction::RunOnReady() {
375  if (!EditBookmarksEnabled())
376    return false;
377
378  scoped_ptr<Cut::Params> params(Cut::Params::Create(*args_));
379  EXTENSION_FUNCTION_VALIDATE(params);
380  return CopyOrCut(true, params->id_list);
381}
382
383bool BookmarkManagerPrivatePasteFunction::RunOnReady() {
384  if (!EditBookmarksEnabled())
385    return false;
386
387  scoped_ptr<Paste::Params> params(Paste::Params::Create(*args_));
388  EXTENSION_FUNCTION_VALIDATE(params);
389  BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
390  const BookmarkNode* parent_node = GetNodeFromString(model, params->parent_id);
391  if (!CanBeModified(parent_node))
392    return false;
393  bool can_paste = bookmarks::CanPasteFromClipboard(model, parent_node);
394  if (!can_paste)
395    return false;
396
397  // We want to use the highest index of the selected nodes as a destination.
398  std::vector<const BookmarkNode*> nodes;
399  // No need to test return value, if we got an empty list, we insert at end.
400  if (params->selected_id_list)
401    GetNodesFromVector(model, *params->selected_id_list, &nodes);
402  int highest_index = -1;  // -1 means insert at end of list.
403  for (size_t i = 0; i < nodes.size(); ++i) {
404    // + 1 so that we insert after the selection.
405    int index = parent_node->GetIndexOf(nodes[i]) + 1;
406    if (index > highest_index)
407      highest_index = index;
408  }
409
410  bookmarks::PasteFromClipboard(model, parent_node, highest_index);
411  return true;
412}
413
414bool BookmarkManagerPrivateCanPasteFunction::RunOnReady() {
415  if (!EditBookmarksEnabled())
416    return false;
417
418  scoped_ptr<CanPaste::Params> params(CanPaste::Params::Create(*args_));
419  EXTENSION_FUNCTION_VALIDATE(params);
420
421  BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
422  const BookmarkNode* parent_node = GetNodeFromString(model, params->parent_id);
423  if (!parent_node) {
424    error_ = bookmark_keys::kNoParentError;
425    return false;
426  }
427  bool can_paste = bookmarks::CanPasteFromClipboard(model, parent_node);
428  SetResult(new base::FundamentalValue(can_paste));
429  return true;
430}
431
432bool BookmarkManagerPrivateSortChildrenFunction::RunOnReady() {
433  if (!EditBookmarksEnabled())
434    return false;
435
436  scoped_ptr<SortChildren::Params> params(SortChildren::Params::Create(*args_));
437  EXTENSION_FUNCTION_VALIDATE(params);
438
439  BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
440  const BookmarkNode* parent_node = GetNodeFromString(model, params->parent_id);
441  if (!CanBeModified(parent_node))
442    return false;
443  model->SortChildren(parent_node);
444  return true;
445}
446
447bool BookmarkManagerPrivateGetStringsFunction::RunAsync() {
448  base::DictionaryValue* localized_strings = new base::DictionaryValue();
449
450  localized_strings->SetString("title",
451      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_TITLE));
452  localized_strings->SetString("search_button",
453      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SEARCH_BUTTON));
454  localized_strings->SetString("organize_menu",
455      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_ORGANIZE_MENU));
456  localized_strings->SetString("show_in_folder",
457      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
458  localized_strings->SetString("sort",
459      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SORT));
460  localized_strings->SetString("import_menu",
461      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_IMPORT_MENU));
462  localized_strings->SetString("export_menu",
463      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_EXPORT_MENU));
464  localized_strings->SetString("rename_folder",
465      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_RENAME_FOLDER));
466  localized_strings->SetString("edit",
467      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_EDIT));
468  localized_strings->SetString("should_open_all",
469      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL));
470  localized_strings->SetString("open_incognito",
471      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_INCOGNITO));
472  localized_strings->SetString("open_in_new_tab",
473      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_IN_NEW_TAB));
474  localized_strings->SetString("open_in_new_window",
475      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_IN_NEW_WINDOW));
476  localized_strings->SetString("add_new_bookmark",
477      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_ADD_NEW_BOOKMARK));
478  localized_strings->SetString("new_folder",
479      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_NEW_FOLDER));
480  localized_strings->SetString("open_all",
481      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_ALL));
482  localized_strings->SetString("open_all_new_window",
483      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW));
484  localized_strings->SetString("open_all_incognito",
485      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_OPEN_ALL_INCOGNITO));
486  localized_strings->SetString("remove",
487      l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_REMOVE));
488  localized_strings->SetString("copy",
489      l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_COPY));
490  localized_strings->SetString("cut",
491      l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_CUT));
492  localized_strings->SetString("paste",
493      l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PASTE));
494  localized_strings->SetString("delete",
495      l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_DELETE));
496  localized_strings->SetString("undo_delete",
497      l10n_util::GetStringUTF16(IDS_UNDO_DELETE));
498  localized_strings->SetString("new_folder_name",
499      l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_NEW_FOLDER_NAME));
500  localized_strings->SetString("name_input_placeholder",
501      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_NAME_INPUT_PLACE_HOLDER));
502  localized_strings->SetString("url_input_placeholder",
503      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_URL_INPUT_PLACE_HOLDER));
504  localized_strings->SetString("invalid_url",
505      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_INVALID_URL));
506  localized_strings->SetString("recent",
507      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_RECENT));
508  localized_strings->SetString("search",
509      l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SEARCH));
510  localized_strings->SetString("save",
511      l10n_util::GetStringUTF16(IDS_SAVE));
512  localized_strings->SetString("cancel",
513      l10n_util::GetStringUTF16(IDS_CANCEL));
514
515  webui::SetFontAndTextDirection(localized_strings);
516
517  SetResult(localized_strings);
518
519  // This is needed because unlike the rest of these functions, this class
520  // inherits from AsyncFunction directly, rather than BookmarkFunction.
521  SendResponse(true);
522
523  return true;
524}
525
526bool BookmarkManagerPrivateStartDragFunction::RunOnReady() {
527  if (!EditBookmarksEnabled())
528    return false;
529
530  scoped_ptr<StartDrag::Params> params(StartDrag::Params::Create(*args_));
531  EXTENSION_FUNCTION_VALIDATE(params);
532
533  BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
534  std::vector<const BookmarkNode*> nodes;
535  EXTENSION_FUNCTION_VALIDATE(
536      GetNodesFromVector(model, params->id_list, &nodes));
537
538  WebContents* web_contents =
539      WebContents::FromRenderViewHost(render_view_host_);
540  if (GetViewType(web_contents) == VIEW_TYPE_TAB_CONTENTS) {
541    WebContents* web_contents =
542        dispatcher()->delegate()->GetAssociatedWebContents();
543    CHECK(web_contents);
544
545    ui::DragDropTypes::DragEventSource source =
546        ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE;
547    if (params->is_from_touch)
548      source = ui::DragDropTypes::DRAG_EVENT_SOURCE_TOUCH;
549
550    chrome::DragBookmarks(
551        GetProfile(), nodes, web_contents->GetNativeView(), source);
552
553    return true;
554  } else {
555    NOTREACHED();
556    return false;
557  }
558}
559
560bool BookmarkManagerPrivateDropFunction::RunOnReady() {
561  if (!EditBookmarksEnabled())
562    return false;
563
564  scoped_ptr<Drop::Params> params(Drop::Params::Create(*args_));
565  EXTENSION_FUNCTION_VALIDATE(params);
566
567  BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
568
569  const BookmarkNode* drop_parent = GetNodeFromString(model, params->parent_id);
570  if (!CanBeModified(drop_parent))
571    return false;
572
573  int drop_index;
574  if (params->index)
575    drop_index = *params->index;
576  else
577    drop_index = drop_parent->child_count();
578
579  WebContents* web_contents =
580      WebContents::FromRenderViewHost(render_view_host_);
581  if (GetViewType(web_contents) == VIEW_TYPE_TAB_CONTENTS) {
582    WebContents* web_contents =
583        dispatcher()->delegate()->GetAssociatedWebContents();
584    CHECK(web_contents);
585    ExtensionWebUI* web_ui =
586        static_cast<ExtensionWebUI*>(web_contents->GetWebUI()->GetController());
587    CHECK(web_ui);
588    BookmarkManagerPrivateDragEventRouter* router =
589        web_ui->bookmark_manager_private_drag_event_router();
590
591    DCHECK(router);
592    const BookmarkNodeData* drag_data = router->GetBookmarkNodeData();
593    if (drag_data == NULL) {
594      NOTREACHED() <<"Somehow we're dropping null bookmark data";
595      return false;
596    }
597    const bool copy = false;
598    chrome::DropBookmarks(
599        GetProfile(), *drag_data, drop_parent, drop_index, copy);
600
601    router->ClearBookmarkNodeData();
602    return true;
603  } else {
604    NOTREACHED();
605    return false;
606  }
607}
608
609bool BookmarkManagerPrivateGetSubtreeFunction::RunOnReady() {
610  scoped_ptr<GetSubtree::Params> params(GetSubtree::Params::Create(*args_));
611  EXTENSION_FUNCTION_VALIDATE(params);
612
613  const BookmarkNode* node = NULL;
614
615  if (params->id == "") {
616    BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
617    node = model->root_node();
618  } else {
619    node = GetBookmarkNodeFromId(params->id);
620    if (!node)
621      return false;
622  }
623
624  std::vector<linked_ptr<api::bookmarks::BookmarkTreeNode> > nodes;
625  ChromeBookmarkClient* client = GetChromeBookmarkClient();
626  if (params->folders_only)
627    bookmark_api_helpers::AddNodeFoldersOnly(client, node, &nodes, true);
628  else
629    bookmark_api_helpers::AddNode(client, node, &nodes, true);
630  results_ = GetSubtree::Results::Create(nodes);
631  return true;
632}
633
634bool BookmarkManagerPrivateCanEditFunction::RunOnReady() {
635  PrefService* prefs = user_prefs::UserPrefs::Get(GetProfile());
636  SetResult(new base::FundamentalValue(
637      prefs->GetBoolean(bookmarks::prefs::kEditBookmarksEnabled)));
638  return true;
639}
640
641bool BookmarkManagerPrivateRecordLaunchFunction::RunOnReady() {
642  RecordBookmarkLaunch(NULL, BOOKMARK_LAUNCH_LOCATION_MANAGER);
643  return true;
644}
645
646bool BookmarkManagerPrivateCreateWithMetaInfoFunction::RunOnReady() {
647  scoped_ptr<CreateWithMetaInfo::Params> params(
648      CreateWithMetaInfo::Params::Create(*args_));
649  EXTENSION_FUNCTION_VALIDATE(params);
650
651  BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
652  const BookmarkNode* node = CreateBookmarkNode(
653      model, params->bookmark, &params->meta_info.additional_properties);
654  if (!node)
655    return false;
656
657  scoped_ptr<api::bookmarks::BookmarkTreeNode> result_node(
658      bookmark_api_helpers::GetBookmarkTreeNode(
659          GetChromeBookmarkClient(), node, false, false));
660  results_ = CreateWithMetaInfo::Results::Create(*result_node);
661
662  return true;
663}
664
665bool BookmarkManagerPrivateGetMetaInfoFunction::RunOnReady() {
666  scoped_ptr<GetMetaInfo::Params> params(GetMetaInfo::Params::Create(*args_));
667  EXTENSION_FUNCTION_VALIDATE(params);
668
669  if (params->id) {
670    const BookmarkNode* node = GetBookmarkNodeFromId(*params->id);
671    if (!node)
672      return false;
673
674    if (params->key) {
675      std::string value;
676      if (node->GetMetaInfo(*params->key, &value)) {
677        GetMetaInfo::Results::Value result;
678        result.as_string.reset(new std::string(value));
679        results_ = GetMetaInfo::Results::Create(result);
680      }
681    } else {
682      GetMetaInfo::Results::Value result;
683      result.as_object.reset(new GetMetaInfo::Results::Value::Object);
684
685      const BookmarkNode::MetaInfoMap* meta_info = node->GetMetaInfoMap();
686      if (meta_info) {
687        BookmarkNode::MetaInfoMap::const_iterator itr;
688        base::DictionaryValue& temp = result.as_object->additional_properties;
689        for (itr = meta_info->begin(); itr != meta_info->end(); itr++) {
690          temp.SetStringWithoutPathExpansion(itr->first, itr->second);
691        }
692      }
693      results_ = GetMetaInfo::Results::Create(result);
694    }
695  } else {
696    if (params->key) {
697      error_ = bookmark_api_constants::kInvalidParamError;
698      return true;
699    }
700
701    BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
702    const BookmarkNode* node = model->root_node();
703
704    GetMetaInfo::Results::Value result;
705    result.as_object.reset(new GetMetaInfo::Results::Value::Object);
706
707    bookmark_api_helpers::GetMetaInfo(*node,
708        &result.as_object->additional_properties);
709
710    results_ = GetMetaInfo::Results::Create(result);
711  }
712
713  return true;
714}
715
716bool BookmarkManagerPrivateSetMetaInfoFunction::RunOnReady() {
717  scoped_ptr<SetMetaInfo::Params> params(SetMetaInfo::Params::Create(*args_));
718  EXTENSION_FUNCTION_VALIDATE(params);
719
720  const BookmarkNode* node = GetBookmarkNodeFromId(params->id);
721  if (!node)
722    return false;
723
724  BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
725  model->SetNodeMetaInfo(node, params->key, params->value);
726  return true;
727}
728
729bool BookmarkManagerPrivateUpdateMetaInfoFunction::RunOnReady() {
730  scoped_ptr<UpdateMetaInfo::Params> params(
731      UpdateMetaInfo::Params::Create(*args_));
732  EXTENSION_FUNCTION_VALIDATE(params);
733
734  const BookmarkNode* node = GetBookmarkNodeFromId(params->id);
735  if (!node)
736    return false;
737
738  BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
739  BookmarkNode::MetaInfoMap new_meta_info(
740      params->meta_info_changes.additional_properties);
741  if (node->GetMetaInfoMap()) {
742    new_meta_info.insert(node->GetMetaInfoMap()->begin(),
743                         node->GetMetaInfoMap()->end());
744  }
745  model->SetNodeMetaInfoMap(node, new_meta_info);
746
747  return true;
748}
749
750bool BookmarkManagerPrivateCanOpenNewWindowsFunction::RunOnReady() {
751  bool can_open_new_windows = true;
752  SetResult(new base::FundamentalValue(can_open_new_windows));
753  return true;
754}
755
756bool BookmarkManagerPrivateRemoveTreesFunction::RunOnReady() {
757  scoped_ptr<RemoveTrees::Params> params(RemoveTrees::Params::Create(*args_));
758  EXTENSION_FUNCTION_VALIDATE(params);
759
760  BookmarkModel* model = GetBookmarkModel();
761  ChromeBookmarkClient* client = GetChromeBookmarkClient();
762  bookmarks::ScopedGroupBookmarkActions group_deletes(model);
763  int64 id;
764  for (size_t i = 0; i < params->id_list.size(); ++i) {
765    if (!GetBookmarkIdAsInt64(params->id_list[i], &id))
766      return false;
767    if (!bookmark_api_helpers::RemoveNode(model, client, id, true, &error_))
768      return false;
769  }
770
771  return true;
772}
773
774bool BookmarkManagerPrivateUndoFunction::RunOnReady() {
775  BookmarkUndoServiceFactory::GetForProfile(GetProfile())->undo_manager()->
776      Undo();
777  return true;
778}
779
780bool BookmarkManagerPrivateRedoFunction::RunOnReady() {
781  BookmarkUndoServiceFactory::GetForProfile(GetProfile())->undo_manager()->
782      Redo();
783  return true;
784}
785
786bool BookmarkManagerPrivateGetUndoInfoFunction::RunOnReady() {
787  UndoManager* undo_manager =
788      BookmarkUndoServiceFactory::GetForProfile(GetProfile())->undo_manager();
789
790  UndoInfo::Results::Result result;
791  result.enabled = undo_manager->undo_count() > 0;
792  result.label = base::UTF16ToUTF8(undo_manager->GetUndoLabel());
793
794  results_ = UndoInfo::Results::Create(result);
795  return true;
796}
797
798bool BookmarkManagerPrivateGetRedoInfoFunction::RunOnReady() {
799  UndoManager* undo_manager =
800      BookmarkUndoServiceFactory::GetForProfile(GetProfile())->undo_manager();
801
802  RedoInfo::Results::Result result;
803  result.enabled = undo_manager->redo_count() > 0;
804  result.label = base::UTF16ToUTF8(undo_manager->GetRedoLabel());
805
806  results_ = RedoInfo::Results::Create(result);
807  return true;
808}
809
810}  // namespace extensions
811