• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_bookmark_manager_api.h"
6 
7 #include <vector>
8 
9 #include "base/json/json_writer.h"
10 #include "base/string_number_conversions.h"
11 #include "base/values.h"
12 #include "chrome/browser/bookmarks/bookmark_model.h"
13 #include "chrome/browser/bookmarks/bookmark_node_data.h"
14 #include "chrome/browser/bookmarks/bookmark_utils.h"
15 #include "chrome/browser/extensions/extension_bookmark_helpers.h"
16 #include "chrome/browser/extensions/extension_bookmarks_module_constants.h"
17 #include "chrome/browser/extensions/extension_event_router.h"
18 #include "chrome/browser/extensions/extension_web_ui.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/ui/webui/chrome_url_data_manager.h"
21 #include "content/browser/renderer_host/render_view_host.h"
22 #include "content/browser/tab_contents/tab_contents.h"
23 #include "grit/generated_resources.h"
24 #include "ui/base/l10n/l10n_util.h"
25 
26 namespace keys = extension_bookmarks_module_constants;
27 
28 namespace {
29 
30 // Returns a single bookmark node from the argument ID.
31 // This returns NULL in case of failure.
GetNodeFromArguments(BookmarkModel * model,const ListValue * args)32 const BookmarkNode* GetNodeFromArguments(BookmarkModel* model,
33     const ListValue* args) {
34   std::string id_string;
35   if (!args->GetString(0, &id_string))
36     return NULL;
37   int64 id;
38   if (!base::StringToInt64(id_string, &id))
39     return NULL;
40   return model->GetNodeByID(id);
41 }
42 
43 // Gets a vector of bookmark nodes from the argument list of IDs.
44 // This returns false in the case of failure.
GetNodesFromArguments(BookmarkModel * model,const ListValue * args,size_t args_index,std::vector<const BookmarkNode * > * nodes)45 bool GetNodesFromArguments(BookmarkModel* model, const ListValue* args,
46     size_t args_index, std::vector<const BookmarkNode*>* nodes) {
47 
48   ListValue* ids;
49   if (!args->GetList(args_index, &ids))
50     return false;
51 
52   size_t count = ids->GetSize();
53   if (count == 0)
54     return false;
55 
56   for (size_t i = 0; i < count; ++i) {
57     std::string id_string;
58     if (!ids->GetString(i, &id_string))
59       return false;
60     int64 id;
61     if (!base::StringToInt64(id_string, &id))
62       return false;
63     const BookmarkNode* node = model->GetNodeByID(id);
64     if (!node)
65       return false;
66     nodes->push_back(node);
67   }
68 
69   return true;
70 }
71 
72 // Recursively adds a node to a list. This is by used |BookmarkNodeDataToJSON|
73 // when the data comes from the current profile. In this case we have a
74 // BookmarkNode since we got the data from the current profile.
AddNodeToList(ListValue * list,const BookmarkNode & node)75 void AddNodeToList(ListValue* list, const BookmarkNode& node) {
76   DictionaryValue* dict = new DictionaryValue();
77 
78   // Add id and parentId so we can associate the data with existing nodes on the
79   // client side.
80   std::string id_string = base::Int64ToString(node.id());
81   dict->SetString(keys::kIdKey, id_string);
82 
83   std::string parent_id_string = base::Int64ToString(node.parent()->id());
84   dict->SetString(keys::kParentIdKey, parent_id_string);
85 
86   if (node.is_url())
87     dict->SetString(keys::kUrlKey, node.GetURL().spec());
88 
89   dict->SetString(keys::kTitleKey, node.GetTitle());
90 
91   ListValue* children = new ListValue();
92   for (int i = 0; i < node.child_count(); ++i)
93     AddNodeToList(children, *node.GetChild(i));
94   dict->Set(keys::kChildrenKey, children);
95 
96   list->Append(dict);
97 }
98 
99 // Recursively adds an element to a list. This is used by
100 // |BookmarkNodeDataToJSON| when the data comes from a different profile. When
101 // the data comes from a different profile we do not have any IDs or parent IDs.
AddElementToList(ListValue * list,const BookmarkNodeData::Element & element)102 void AddElementToList(ListValue* list,
103                       const BookmarkNodeData::Element& element) {
104   DictionaryValue* dict = new DictionaryValue();
105 
106   if (element.is_url)
107     dict->SetString(keys::kUrlKey, element.url.spec());
108 
109   dict->SetString(keys::kTitleKey, element.title);
110 
111   ListValue* children = new ListValue();
112   for (size_t i = 0; i < element.children.size(); ++i)
113     AddElementToList(children, element.children[i]);
114   dict->Set(keys::kChildrenKey, children);
115 
116   list->Append(dict);
117 }
118 
119 // Builds the JSON structure based on the BookmarksDragData.
BookmarkNodeDataToJSON(Profile * profile,const BookmarkNodeData & data,ListValue * args)120 void BookmarkNodeDataToJSON(Profile* profile, const BookmarkNodeData& data,
121                             ListValue* args) {
122   bool same_profile = data.IsFromProfile(profile);
123   DictionaryValue* value = new DictionaryValue();
124   value->SetBoolean(keys::kSameProfileKey, same_profile);
125 
126   ListValue* list = new ListValue();
127   if (same_profile) {
128     std::vector<const BookmarkNode*> nodes = data.GetNodes(profile);
129     for (size_t i = 0; i < nodes.size(); ++i)
130       AddNodeToList(list, *nodes[i]);
131   } else {
132     // We do not have an node IDs when the data comes from a different profile.
133     std::vector<BookmarkNodeData::Element> elements = data.elements;
134     for (size_t i = 0; i < elements.size(); ++i)
135       AddElementToList(list, elements[i]);
136   }
137   value->Set(keys::kElementsKey, list);
138 
139   args->Append(value);
140 }
141 
142 }  // namespace
143 
ExtensionBookmarkManagerEventRouter(Profile * profile,TabContents * tab_contents)144 ExtensionBookmarkManagerEventRouter::ExtensionBookmarkManagerEventRouter(
145     Profile* profile, TabContents* tab_contents)
146     : profile_(profile),
147     tab_contents_(tab_contents) {
148   tab_contents_->SetBookmarkDragDelegate(this);
149 }
150 
~ExtensionBookmarkManagerEventRouter()151 ExtensionBookmarkManagerEventRouter::~ExtensionBookmarkManagerEventRouter() {
152   if (tab_contents_->GetBookmarkDragDelegate() == this)
153     tab_contents_->SetBookmarkDragDelegate(NULL);
154 }
155 
DispatchEvent(const char * event_name,const ListValue * args)156 void ExtensionBookmarkManagerEventRouter::DispatchEvent(const char* event_name,
157                                                         const ListValue* args) {
158   if (!profile_->GetExtensionEventRouter())
159     return;
160 
161   std::string json_args;
162   base::JSONWriter::Write(args, false, &json_args);
163   profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
164       event_name, json_args, NULL, GURL());
165 }
166 
DispatchDragEvent(const BookmarkNodeData & data,const char * event_name)167 void ExtensionBookmarkManagerEventRouter::DispatchDragEvent(
168     const BookmarkNodeData& data, const char* event_name) {
169   if (data.size() == 0)
170     return;
171 
172   ListValue args;
173   BookmarkNodeDataToJSON(profile_, data, &args);
174   DispatchEvent(event_name, &args);
175 }
176 
OnDragEnter(const BookmarkNodeData & data)177 void ExtensionBookmarkManagerEventRouter::OnDragEnter(
178     const BookmarkNodeData& data) {
179   DispatchDragEvent(data, keys::kOnBookmarkDragEnter);
180 }
181 
OnDragOver(const BookmarkNodeData & data)182 void ExtensionBookmarkManagerEventRouter::OnDragOver(
183     const BookmarkNodeData& data) {
184   // Intentionally empty since these events happens too often and floods the
185   // message queue. We do not need this event for the bookmark manager anyway.
186 }
187 
OnDragLeave(const BookmarkNodeData & data)188 void ExtensionBookmarkManagerEventRouter::OnDragLeave(
189     const BookmarkNodeData& data) {
190   DispatchDragEvent(data, keys::kOnBookmarkDragLeave);
191 }
192 
OnDrop(const BookmarkNodeData & data)193 void ExtensionBookmarkManagerEventRouter::OnDrop(
194     const BookmarkNodeData& data) {
195   DispatchDragEvent(data, keys::kOnBookmarkDrop);
196 
197   // Make a copy that is owned by this instance.
198   ClearBookmarkNodeData();
199   bookmark_drag_data_ = data;
200 }
201 
202 const BookmarkNodeData*
GetBookmarkNodeData()203 ExtensionBookmarkManagerEventRouter::GetBookmarkNodeData() {
204   if (bookmark_drag_data_.is_valid())
205     return &bookmark_drag_data_;
206   return NULL;
207 }
208 
ClearBookmarkNodeData()209 void ExtensionBookmarkManagerEventRouter::ClearBookmarkNodeData() {
210   bookmark_drag_data_.Clear();
211 }
212 
CopyOrCut(bool cut)213 bool ClipboardBookmarkManagerFunction::CopyOrCut(bool cut) {
214   BookmarkModel* model = profile()->GetBookmarkModel();
215   std::vector<const BookmarkNode*> nodes;
216   EXTENSION_FUNCTION_VALIDATE(GetNodesFromArguments(model, args_.get(),
217                                                     0, &nodes));
218   bookmark_utils::CopyToClipboard(model, nodes, cut);
219   return true;
220 }
221 
RunImpl()222 bool CopyBookmarkManagerFunction::RunImpl() {
223   return CopyOrCut(false);
224 }
225 
RunImpl()226 bool CutBookmarkManagerFunction::RunImpl() {
227   if (!EditBookmarksEnabled())
228     return false;
229   return CopyOrCut(true);
230 }
231 
RunImpl()232 bool PasteBookmarkManagerFunction::RunImpl() {
233   if (!EditBookmarksEnabled())
234     return false;
235   BookmarkModel* model = profile()->GetBookmarkModel();
236   const BookmarkNode* parent_node = GetNodeFromArguments(model, args_.get());
237   if (!parent_node) {
238     error_ = keys::kNoParentError;
239     return false;
240   }
241   bool can_paste = bookmark_utils::CanPasteFromClipboard(parent_node);
242   if (!can_paste)
243     return false;
244 
245   // We want to use the highest index of the selected nodes as a destination.
246   std::vector<const BookmarkNode*> nodes;
247   // No need to test return value, if we got an empty list, we insert at end.
248   GetNodesFromArguments(model, args_.get(), 1, &nodes);
249   int highest_index = -1;  // -1 means insert at end of list.
250   for (size_t node = 0; node < nodes.size(); ++node) {
251     // + 1 so that we insert after the selection.
252     int this_node_index = parent_node->GetIndexOf(nodes[node]) + 1;
253     if (this_node_index > highest_index)
254       highest_index = this_node_index;
255   }
256 
257   bookmark_utils::PasteFromClipboard(model, parent_node, highest_index);
258   return true;
259 }
260 
RunImpl()261 bool CanPasteBookmarkManagerFunction::RunImpl() {
262   if (!EditBookmarksEnabled())
263     return false;
264   BookmarkModel* model = profile()->GetBookmarkModel();
265   const BookmarkNode* parent_node = GetNodeFromArguments(model, args_.get());
266   if (!parent_node) {
267     error_ = keys::kNoParentError;
268     return false;
269   }
270   bool can_paste = bookmark_utils::CanPasteFromClipboard(parent_node);
271   result_.reset(Value::CreateBooleanValue(can_paste));
272   SendResponse(true);
273   return true;
274 }
275 
RunImpl()276 bool SortChildrenBookmarkManagerFunction::RunImpl() {
277   if (!EditBookmarksEnabled())
278     return false;
279   BookmarkModel* model = profile()->GetBookmarkModel();
280   const BookmarkNode* parent_node = GetNodeFromArguments(model, args_.get());
281   if (!parent_node) {
282     error_ = keys::kNoParentError;
283     return false;
284   }
285   model->SortChildren(parent_node);
286   return true;
287 }
288 
RunImpl()289 bool BookmarkManagerGetStringsFunction::RunImpl() {
290   DictionaryValue* localized_strings = new DictionaryValue();
291 
292   localized_strings->SetString("title",
293       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_TITLE));
294   localized_strings->SetString("search_button",
295       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SEARCH_BUTTON));
296   localized_strings->SetString("show_in_folder",
297       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
298   localized_strings->SetString("sort",
299       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SORT));
300   localized_strings->SetString("organize_menu",
301       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_ORGANIZE_MENU));
302   localized_strings->SetString("tools_menu",
303       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_TOOLS_MENU));
304   localized_strings->SetString("import_menu",
305       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_IMPORT_MENU));
306   localized_strings->SetString("export_menu",
307       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_EXPORT_MENU));
308   localized_strings->SetString("rename_folder",
309       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_RENAME_FOLDER));
310   localized_strings->SetString("edit",
311       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_EDIT));
312   localized_strings->SetString("should_open_all",
313       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL));
314   localized_strings->SetString("open_incognito",
315       l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OPEN_INCOGNITO));
316   localized_strings->SetString("open_in_new_tab",
317       l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OPEN_IN_NEW_TAB));
318   localized_strings->SetString("open_in_new_window",
319       l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OPEN_IN_NEW_WINDOW));
320   localized_strings->SetString("add_new_bookmark",
321       l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK));
322   localized_strings->SetString("new_folder",
323       l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_NEW_FOLDER));
324   localized_strings->SetString("open_all",
325       l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OPEN_ALL));
326   localized_strings->SetString("open_all_new_window",
327       l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
328   localized_strings->SetString("open_all_incognito",
329       l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
330   localized_strings->SetString("remove",
331       l10n_util::GetStringUTF16(IDS_BOOKMARK_BAR_REMOVE));
332   localized_strings->SetString("copy",
333       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_COPY));
334   localized_strings->SetString("cut",
335       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_CUT));
336   localized_strings->SetString("paste",
337       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PASTE));
338   localized_strings->SetString("delete",
339       l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_DELETE));
340   localized_strings->SetString("new_folder_name",
341       l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME));
342   localized_strings->SetString("name_input_placeholder",
343       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_NAME_INPUT_PLACE_HOLDER));
344   localized_strings->SetString("url_input_placeholder",
345       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_URL_INPUT_PLACE_HOLDER));
346   localized_strings->SetString("invalid_url",
347       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_INVALID_URL));
348   localized_strings->SetString("recent",
349       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_RECENT));
350   localized_strings->SetString("search",
351       l10n_util::GetStringUTF16(IDS_BOOKMARK_MANAGER_SEARCH));
352 
353   ChromeURLDataManager::DataSource::SetFontAndTextDirection(localized_strings);
354 
355   result_.reset(localized_strings);
356   SendResponse(true);
357   return true;
358 }
359 
RunImpl()360 bool StartDragBookmarkManagerFunction::RunImpl() {
361   if (!EditBookmarksEnabled())
362     return false;
363   BookmarkModel* model = profile()->GetBookmarkModel();
364   std::vector<const BookmarkNode*> nodes;
365   EXTENSION_FUNCTION_VALIDATE(
366       GetNodesFromArguments(model, args_.get(), 0, &nodes));
367 
368   if (dispatcher()->render_view_host()->delegate()->GetRenderViewType() ==
369       ViewType::TAB_CONTENTS) {
370     ExtensionWebUI* web_ui =
371         static_cast<ExtensionWebUI*>(dispatcher()->delegate());
372     bookmark_utils::DragBookmarks(
373         profile(), nodes, web_ui->tab_contents()->GetNativeView());
374 
375     return true;
376   } else {
377     NOTREACHED();
378     return false;
379   }
380 }
381 
RunImpl()382 bool DropBookmarkManagerFunction::RunImpl() {
383   if (!EditBookmarksEnabled())
384     return false;
385 
386   BookmarkModel* model = profile()->GetBookmarkModel();
387 
388   int64 id;
389   std::string id_string;
390   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &id_string));
391 
392   if (!base::StringToInt64(id_string, &id)) {
393     error_ = keys::kInvalidIdError;
394     return false;
395   }
396 
397   const BookmarkNode* drop_parent = model->GetNodeByID(id);
398   if (!drop_parent) {
399     error_ = keys::kNoParentError;
400     return false;
401   }
402 
403   int drop_index;
404   if (args_->GetSize() == 2)
405     EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(1, &drop_index));
406   else
407     drop_index = drop_parent->child_count();
408 
409   if (dispatcher()->render_view_host()->delegate()->GetRenderViewType() ==
410       ViewType::TAB_CONTENTS) {
411     ExtensionWebUI* web_ui =
412         static_cast<ExtensionWebUI*>(dispatcher()->delegate());
413     ExtensionBookmarkManagerEventRouter* router =
414         web_ui->extension_bookmark_manager_event_router();
415 
416     DCHECK(router);
417     const BookmarkNodeData* drag_data = router->GetBookmarkNodeData();
418     if (drag_data == NULL) {
419       NOTREACHED() <<"Somehow we're dropping null bookmark data";
420       return false;
421     }
422     bookmark_utils::PerformBookmarkDrop(profile(),
423                                         *drag_data,
424                                         drop_parent, drop_index);
425 
426     router->ClearBookmarkNodeData();
427     SendResponse(true);
428     return true;
429   } else {
430     NOTREACHED();
431     return false;
432   }
433 }
434 
RunImpl()435 bool GetSubtreeBookmarkManagerFunction::RunImpl() {
436   BookmarkModel* model = profile()->GetBookmarkModel();
437   const BookmarkNode* node;
438   int64 id;
439   std::string id_string;
440   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &id_string));
441   bool folders_only;
442   EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &folders_only));
443   if (id_string == "") {
444     node = model->root_node();
445   } else {
446      if (!base::StringToInt64(id_string, &id)) {
447       error_ = keys::kInvalidIdError;
448       return false;
449     }
450     node = model->GetNodeByID(id);
451   }
452   if (!node) {
453     error_ = keys::kNoNodeError;
454     return false;
455   }
456   scoped_ptr<ListValue> json(new ListValue());
457   if (folders_only) {
458     extension_bookmark_helpers::AddNodeFoldersOnly(node,
459                                                    json.get(),
460                                                    true);
461   } else {
462     extension_bookmark_helpers::AddNode(node, json.get(), true);
463   }
464   result_.reset(json.release());
465   return true;
466 }
467