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