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.
5 #include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h"
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"
40 #include "win8/util/win8_util.h"
43 namespace extensions {
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;
58 using content::WebContents;
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) {
67 if (!base::StringToInt64(id_string, &id))
69 return model->GetNodeByID(id);
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) {
78 if (id_strings.empty())
81 for (size_t i = 0; i < id_strings.size(); ++i) {
82 const BookmarkNode* node = GetNodeFromString(model, id_strings[i]);
85 nodes->push_back(node);
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();
97 // Add id and parentId so we can associate the data with existing nodes on the
99 std::string id_string = base::Int64ToString(node.id());
100 dict->SetString(bookmark_keys::kIdKey, id_string);
102 std::string parent_id_string = base::Int64ToString(node.parent()->id());
103 dict->SetString(bookmark_keys::kParentIdKey, parent_id_string);
106 dict->SetString(bookmark_keys::kUrlKey, node.url().spec());
108 dict->SetString(bookmark_keys::kTitleKey, node.GetTitle());
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);
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();
126 dict->SetString(bookmark_keys::kUrlKey, element.url.spec());
128 dict->SetString(bookmark_keys::kTitleKey, element.title);
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);
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);
145 base::ListValue* list = new base::ListValue();
147 std::vector<const BookmarkNode*> nodes = data.GetNodes(profile);
148 for (size_t i = 0; i < nodes.size(); ++i)
149 AddNodeToList(list, *nodes[i]);
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]);
156 value->Set(manager_keys::kElementsKey, list);
163 BookmarkManagerPrivateEventRouter::BookmarkManagerPrivateEventRouter(
165 content::WebContents* web_contents)
167 web_contents_(web_contents) {
168 BookmarkTabHelper* bookmark_tab_helper =
169 BookmarkTabHelper::FromWebContents(web_contents_);
170 bookmark_tab_helper->set_bookmark_drag_delegate(this);
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);
180 void BookmarkManagerPrivateEventRouter::DispatchEvent(
181 const std::string& event_name,
182 scoped_ptr<base::ListValue> args) {
183 if (!ExtensionSystem::Get(profile_)->event_router())
186 scoped_ptr<Event> event(new Event(event_name, args.Pass()));
187 ExtensionSystem::Get(profile_)->event_router()->BroadcastEvent(event.Pass());
190 void BookmarkManagerPrivateEventRouter::DispatchDragEvent(
191 const BookmarkNodeData& data,
192 const std::string& event_name) {
193 if (data.size() == 0)
196 scoped_ptr<base::ListValue> args(new base::ListValue());
197 BookmarkNodeDataToJSON(profile_, data, args.get());
198 DispatchEvent(event_name, args.Pass());
201 void BookmarkManagerPrivateEventRouter::OnDragEnter(
202 const BookmarkNodeData& data) {
203 DispatchDragEvent(data, bookmark_manager_private::OnDragEnter::kEventName);
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.
212 void BookmarkManagerPrivateEventRouter::OnDragLeave(
213 const BookmarkNodeData& data) {
214 DispatchDragEvent(data, bookmark_manager_private::OnDragLeave::kEventName);
217 void BookmarkManagerPrivateEventRouter::OnDrop(const BookmarkNodeData& data) {
218 DispatchDragEvent(data, bookmark_manager_private::OnDrop::kEventName);
220 // Make a copy that is owned by this instance.
221 ClearBookmarkNodeData();
222 bookmark_drag_data_ = data;
225 const BookmarkNodeData*
226 BookmarkManagerPrivateEventRouter::GetBookmarkNodeData() {
227 if (bookmark_drag_data_.is_valid())
228 return &bookmark_drag_data_;
232 void BookmarkManagerPrivateEventRouter::ClearBookmarkNodeData() {
233 bookmark_drag_data_.Clear();
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);
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);
251 bool BookmarkManagerPrivateCutFunction::RunImpl() {
252 if (!EditBookmarksEnabled())
255 scoped_ptr<Cut::Params> params(Cut::Params::Create(*args_));
256 EXTENSION_FUNCTION_VALIDATE(params);
257 return CopyOrCut(true, params->id_list);
260 bool BookmarkManagerPrivatePasteFunction::RunImpl() {
261 if (!EditBookmarksEnabled())
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);
269 error_ = bookmark_keys::kNoParentError;
272 bool can_paste = bookmark_utils::CanPasteFromClipboard(parent_node);
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;
289 bookmark_utils::PasteFromClipboard(model, parent_node, highest_index);
293 bool BookmarkManagerPrivateCanPasteFunction::RunImpl() {
294 if (!EditBookmarksEnabled())
297 scoped_ptr<CanPaste::Params> params(CanPaste::Params::Create(*args_));
298 EXTENSION_FUNCTION_VALIDATE(params);
300 BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
301 const BookmarkNode* parent_node = GetNodeFromString(model, params->parent_id);
303 error_ = bookmark_keys::kNoParentError;
306 bool can_paste = bookmark_utils::CanPasteFromClipboard(parent_node);
307 SetResult(new base::FundamentalValue(can_paste));
311 bool BookmarkManagerPrivateSortChildrenFunction::RunImpl() {
312 if (!EditBookmarksEnabled())
315 scoped_ptr<SortChildren::Params> params(SortChildren::Params::Create(*args_));
316 EXTENSION_FUNCTION_VALIDATE(params);
318 BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
319 const BookmarkNode* parent_node = GetNodeFromString(model, params->parent_id);
321 error_ = bookmark_keys::kNoParentError;
324 model->SortChildren(parent_node);
328 bool BookmarkManagerPrivateGetStringsFunction::RunImpl() {
329 base::DictionaryValue* localized_strings = new base::DictionaryValue();
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));
396 webui::SetFontAndTextDirection(localized_strings);
398 SetResult(localized_strings);
400 // This is needed because unlike the rest of these functions, this class
401 // inherits from AsyncFunction directly, rather than BookmarkFunction.
407 bool BookmarkManagerPrivateStartDragFunction::RunImpl() {
408 if (!EditBookmarksEnabled())
411 scoped_ptr<StartDrag::Params> params(StartDrag::Params::Create(*args_));
412 EXTENSION_FUNCTION_VALIDATE(params);
414 BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
415 std::vector<const BookmarkNode*> nodes;
416 EXTENSION_FUNCTION_VALIDATE(
417 GetNodesFromVector(model, params->id_list, &nodes));
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();
425 chrome::DragBookmarks(
426 GetProfile(), nodes, web_contents->GetView()->GetNativeView());
435 bool BookmarkManagerPrivateDropFunction::RunImpl() {
436 if (!EditBookmarksEnabled())
439 scoped_ptr<Drop::Params> params(Drop::Params::Create(*args_));
440 EXTENSION_FUNCTION_VALIDATE(params);
442 BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
444 const BookmarkNode* drop_parent = GetNodeFromString(model, params->parent_id);
446 error_ = bookmark_keys::kNoParentError;
452 drop_index = *params->index;
454 drop_index = drop_parent->child_count();
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();
462 ExtensionWebUI* web_ui =
463 static_cast<ExtensionWebUI*>(web_contents->GetWebUI()->GetController());
465 BookmarkManagerPrivateEventRouter* router =
466 web_ui->bookmark_manager_private_event_router();
469 const BookmarkNodeData* drag_data = router->GetBookmarkNodeData();
470 if (drag_data == NULL) {
471 NOTREACHED() <<"Somehow we're dropping null bookmark data";
474 chrome::DropBookmarks(GetProfile(), *drag_data, drop_parent, drop_index);
476 router->ClearBookmarkNodeData();
484 bool BookmarkManagerPrivateGetSubtreeFunction::RunImpl() {
485 scoped_ptr<GetSubtree::Params> params(GetSubtree::Params::Create(*args_));
486 EXTENSION_FUNCTION_VALIDATE(params);
488 BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
489 const BookmarkNode* node = NULL;
491 if (params->id == "") {
492 node = model->root_node();
495 if (!base::StringToInt64(params->id, &id)) {
496 error_ = bookmark_keys::kInvalidIdError;
499 node = model->GetNodeByID(id);
503 error_ = bookmark_keys::kNoNodeError;
507 scoped_ptr<base::ListValue> json(new base::ListValue());
508 if (params->folders_only)
509 bookmark_api_helpers::AddNodeFoldersOnly(node, json.get(), true);
511 bookmark_api_helpers::AddNode(node, json.get(), true);
512 SetResult(json.release());
516 bool BookmarkManagerPrivateCanEditFunction::RunImpl() {
517 PrefService* prefs = user_prefs::UserPrefs::Get(GetProfile());
518 SetResult(new base::FundamentalValue(
519 prefs->GetBoolean(prefs::kEditBookmarksEnabled)));
523 bool BookmarkManagerPrivateRecordLaunchFunction::RunImpl() {
524 RecordBookmarkLaunch(NULL, BOOKMARK_LAUNCH_LOCATION_MANAGER);
528 bool BookmarkManagerPrivateCanOpenNewWindowsFunction::RunImpl() {
529 bool can_open_new_windows = true;
532 if (win8::IsSingleWindowMetroMode())
533 can_open_new_windows = false;
536 SetResult(new base::FundamentalValue(can_open_new_windows));
540 bool BookmarkManagerPrivateRemoveTreesFunction::RunImpl() {
541 scoped_ptr<RemoveTrees::Params> params(RemoveTrees::Params::Create(*args_));
542 EXTENSION_FUNCTION_VALIDATE(params);
544 BookmarkModel* model = BookmarkModelFactory::GetForProfile(GetProfile());
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;
551 if (!bookmark_api_helpers::RemoveNode(model, id, true, &error_))
558 } // namespace extensions