| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/extensions/extension_bookmark_manager_api.h" |
| |
| #include <vector> |
| |
| #include "app/l10n_util.h" |
| #include "base/json/json_writer.h" |
| #include "base/string_util.h" |
| #include "base/values.h" |
| #include "chrome/browser/bookmarks/bookmark_drag_data.h" |
| #include "chrome/browser/bookmarks/bookmark_model.h" |
| #include "chrome/browser/bookmarks/bookmark_utils.h" |
| #include "chrome/browser/dom_ui/chrome_url_data_manager.h" |
| #include "chrome/browser/extensions/extension_bookmark_helpers.h" |
| #include "chrome/browser/extensions/extension_bookmarks_module_constants.h" |
| #include "chrome/browser/extensions/extension_dom_ui.h" |
| #include "chrome/browser/extensions/extension_message_service.h" |
| #include "chrome/browser/profile.h" |
| #include "chrome/browser/renderer_host/render_view_host.h" |
| #include "chrome/browser/tab_contents/tab_contents.h" |
| #include "grit/generated_resources.h" |
| |
| namespace keys = extension_bookmarks_module_constants; |
| |
| namespace { |
| |
| // Returns a single bookmark node from the argument ID. |
| // This returns NULL in case of failure. |
| const BookmarkNode* GetNodeFromArguments(BookmarkModel* model, |
| const Value* args) { |
| std::string id_string; |
| if (!args->GetAsString(&id_string)) |
| return NULL; |
| int64 id; |
| if (!StringToInt64(id_string, &id)) |
| return NULL; |
| return model->GetNodeByID(id); |
| } |
| |
| // Gets a vector of bookmark nodes from the argument list of IDs. |
| // This returns false in the case of failure. |
| bool GetNodesFromArguments(BookmarkModel* model, const ListValue* ids, |
| std::vector<const BookmarkNode*>* nodes) { |
| size_t count = ids->GetSize(); |
| if (count == 0) |
| return false; |
| |
| for (size_t i = 0; i < count; ++i) { |
| std::string id_string; |
| if (!ids->GetString(i, &id_string)) |
| return false; |
| int64 id; |
| if (!StringToInt64(id_string, &id)) |
| return false; |
| const BookmarkNode* node = model->GetNodeByID(id); |
| if (!node) |
| return false; |
| nodes->push_back(node); |
| } |
| |
| return true; |
| } |
| |
| // Recursively adds a node to a list. This is by used |BookmarkDragDataToJSON| |
| // when the data comes from the current profile. In this case we have a |
| // BookmarkNode since we got the data from the current profile. |
| void AddNodeToList(ListValue* list, const BookmarkNode& node) { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| // Add id and parentId so we can associate the data with existing nodes on the |
| // client side. |
| std::string id_string = Int64ToString(node.id()); |
| dict->SetString(keys::kIdKey, id_string); |
| |
| std::string parent_id_string = Int64ToString(node.GetParent()->id()); |
| dict->SetString(keys::kParentIdKey, parent_id_string); |
| |
| if (node.is_url()) |
| dict->SetString(keys::kUrlKey, node.GetURL().spec()); |
| |
| dict->SetString(keys::kTitleKey, node.GetTitle()); |
| |
| ListValue* children = new ListValue(); |
| for (int i = 0; i < node.GetChildCount(); ++i) |
| AddNodeToList(children, *node.GetChild(i)); |
| dict->Set(keys::kChildrenKey, children); |
| |
| list->Append(dict); |
| } |
| |
| // Recursively adds an element to a list. This is by used |
| // |BookmarkDragDataToJSON| when the data comes from a different profile. When |
| // the data comes from a different profile we do not have any IDs or parent IDs. |
| void AddElementToList(ListValue* list, |
| const BookmarkDragData::Element& element) { |
| DictionaryValue* dict = new DictionaryValue(); |
| |
| if (element.is_url) |
| dict->SetString(keys::kUrlKey, element.url.spec()); |
| |
| dict->SetString(keys::kTitleKey, UTF16ToWide(element.title)); |
| |
| ListValue* children = new ListValue(); |
| for (size_t i = 0; i < element.children.size(); ++i) |
| AddElementToList(children, element.children[i]); |
| dict->Set(keys::kChildrenKey, children); |
| |
| list->Append(dict); |
| } |
| |
| // Builds the JSON structure based on the BookmarksDragData. |
| void BookmarkDragDataToJSON(Profile* profile, const BookmarkDragData& data, |
| ListValue* args) { |
| bool same_profile = data.IsFromProfile(profile); |
| DictionaryValue* value = new DictionaryValue(); |
| value->SetBoolean(keys::kSameProfileKey, same_profile); |
| |
| ListValue* list = new ListValue(); |
| if (same_profile) { |
| std::vector<const BookmarkNode*> nodes = data.GetNodes(profile); |
| for (size_t i = 0; i < nodes.size(); ++i) |
| AddNodeToList(list, *nodes[i]); |
| } else { |
| // We do not have an node IDs when the data comes from a different profile. |
| std::vector<BookmarkDragData::Element> elements = data.elements; |
| for (size_t i = 0; i < elements.size(); ++i) |
| AddElementToList(list, elements[i]); |
| } |
| value->Set(keys::kElementsKey, list); |
| |
| args->Append(value); |
| } |
| |
| } // namespace |
| |
| ExtensionBookmarkManagerEventRouter::ExtensionBookmarkManagerEventRouter( |
| Profile* profile, TabContents* tab_contents) |
| : profile_(profile), |
| tab_contents_(tab_contents) { |
| tab_contents_->SetBookmarkDragDelegate(this); |
| } |
| |
| ExtensionBookmarkManagerEventRouter::~ExtensionBookmarkManagerEventRouter() { |
| if (tab_contents_->GetBookmarkDragDelegate() == this) |
| tab_contents_->SetBookmarkDragDelegate(NULL); |
| } |
| |
| void ExtensionBookmarkManagerEventRouter::DispatchEvent(const char* event_name, |
| const ListValue* args) { |
| if (!profile_->GetExtensionMessageService()) |
| return; |
| |
| std::string json_args; |
| base::JSONWriter::Write(args, false, &json_args); |
| profile_->GetExtensionMessageService()->DispatchEventToRenderers( |
| event_name, json_args, profile_->IsOffTheRecord()); |
| } |
| |
| void ExtensionBookmarkManagerEventRouter::DispatchDragEvent( |
| const BookmarkDragData& data, const char* event_name) { |
| if (data.size() == 0) |
| return; |
| |
| ListValue args; |
| BookmarkDragDataToJSON(profile_, data, &args); |
| DispatchEvent(event_name, &args); |
| } |
| |
| void ExtensionBookmarkManagerEventRouter::OnDragEnter( |
| const BookmarkDragData& data) { |
| DispatchDragEvent(data, keys::kOnBookmarkDragEnter); |
| } |
| |
| void ExtensionBookmarkManagerEventRouter::OnDragOver( |
| const BookmarkDragData& data) { |
| // Intentionally empty since these events happens too often and floods the |
| // message queue. We do not need this event for the bookmark manager anyway. |
| } |
| |
| void ExtensionBookmarkManagerEventRouter::OnDragLeave( |
| const BookmarkDragData& data) { |
| DispatchDragEvent(data, keys::kOnBookmarkDragLeave); |
| } |
| |
| void ExtensionBookmarkManagerEventRouter::OnDrop( |
| const BookmarkDragData& data) { |
| DispatchDragEvent(data, keys::kOnBookmarkDrop); |
| |
| // Make a copy that is owned by this instance. |
| ClearBookmarkDragData(); |
| bookmark_drag_data_ = data; |
| } |
| |
| const BookmarkDragData* |
| ExtensionBookmarkManagerEventRouter::GetBookmarkDragData() { |
| if (bookmark_drag_data_.is_valid()) |
| return &bookmark_drag_data_; |
| return NULL; |
| } |
| |
| void ExtensionBookmarkManagerEventRouter::ClearBookmarkDragData() { |
| bookmark_drag_data_.Clear(); |
| } |
| |
| bool ClipboardBookmarkManagerFunction::CopyOrCut(bool cut) { |
| BookmarkModel* model = profile()->GetBookmarkModel(); |
| std::vector<const BookmarkNode*> nodes; |
| EXTENSION_FUNCTION_VALIDATE(GetNodesFromArguments(model, args_as_list(), |
| &nodes)); |
| bookmark_utils::CopyToClipboard(model, nodes, cut); |
| return true; |
| } |
| |
| bool CopyBookmarkManagerFunction::RunImpl() { |
| return CopyOrCut(false); |
| } |
| |
| bool CutBookmarkManagerFunction::RunImpl() { |
| return CopyOrCut(true); |
| } |
| |
| bool PasteBookmarkManagerFunction::RunImpl() { |
| BookmarkModel* model = profile()->GetBookmarkModel(); |
| const BookmarkNode* parent_node = GetNodeFromArguments(model, args_.get()); |
| if (!parent_node) { |
| error_ = keys::kNoParentError; |
| return false; |
| } |
| bool can_paste = bookmark_utils::CanPasteFromClipboard(parent_node); |
| if (!can_paste) |
| return false; |
| bookmark_utils::PasteFromClipboard(model, parent_node, -1); |
| return true; |
| } |
| |
| bool CanPasteBookmarkManagerFunction::RunImpl() { |
| BookmarkModel* model = profile()->GetBookmarkModel(); |
| const BookmarkNode* parent_node = GetNodeFromArguments(model, args_.get()); |
| if (!parent_node) { |
| error_ = keys::kNoParentError; |
| return false; |
| } |
| bool can_paste = bookmark_utils::CanPasteFromClipboard(parent_node); |
| result_.reset(Value::CreateBooleanValue(can_paste)); |
| SendResponse(true); |
| return true; |
| } |
| |
| bool SortChildrenBookmarkManagerFunction::RunImpl() { |
| BookmarkModel* model = profile()->GetBookmarkModel(); |
| const BookmarkNode* parent_node = GetNodeFromArguments(model, args_.get()); |
| if (!parent_node) { |
| error_ = keys::kNoParentError; |
| return false; |
| } |
| model->SortChildren(parent_node); |
| return true; |
| } |
| |
| bool BookmarkManagerGetStringsFunction::RunImpl() { |
| DictionaryValue* localized_strings = new DictionaryValue(); |
| |
| localized_strings->SetString(L"title", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_TITLE)); |
| localized_strings->SetString(L"search_button", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_SEARCH_BUTTON)); |
| localized_strings->SetString(L"show_in_folder", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER)); |
| localized_strings->SetString(L"sort", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_SORT)); |
| localized_strings->SetString(L"organize_menu", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_ORGANIZE_MENU)); |
| localized_strings->SetString(L"tools_menu", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_TOOLS_MENU)); |
| localized_strings->SetString(L"import_menu", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_IMPORT_MENU)); |
| localized_strings->SetString(L"export_menu", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_EXPORT_MENU)); |
| |
| localized_strings->SetString(L"rename_folder", |
| l10n_util::GetString(IDS_BOOKMARK_BAR_RENAME_FOLDER)); |
| localized_strings->SetString(L"edit", |
| l10n_util::GetString(IDS_BOOKMARK_BAR_EDIT)); |
| localized_strings->SetString(L"should_open_all", |
| l10n_util::GetString(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL)); |
| localized_strings->SetString(L"open_incognito", |
| l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_INCOGNITO)); |
| localized_strings->SetString(L"open_in_new_tab", |
| l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_IN_NEW_TAB)); |
| localized_strings->SetString(L"open_in_new_window", |
| l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_IN_NEW_WINDOW)); |
| localized_strings->SetString(L"add_new_bookmark", |
| l10n_util::GetString(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK)); |
| localized_strings->SetString(L"new_folder", |
| l10n_util::GetString(IDS_BOOMARK_BAR_NEW_FOLDER)); |
| localized_strings->SetString(L"open_all", |
| l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL)); |
| localized_strings->SetString(L"open_all_new_window", |
| l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW)); |
| localized_strings->SetString(L"open_all_incognito", |
| l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO)); |
| localized_strings->SetString(L"remove", |
| l10n_util::GetString(IDS_BOOKMARK_BAR_REMOVE)); |
| localized_strings->SetString(L"copy", |
| l10n_util::GetString(IDS_CONTENT_CONTEXT_COPY)); |
| localized_strings->SetString(L"cut", |
| l10n_util::GetString(IDS_CONTENT_CONTEXT_CUT)); |
| localized_strings->SetString(L"paste", |
| l10n_util::GetString(IDS_CONTENT_CONTEXT_PASTE)); |
| localized_strings->SetString(L"delete", |
| l10n_util::GetString(IDS_CONTENT_CONTEXT_DELETE)); |
| localized_strings->SetString(L"new_folder_name", |
| l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME)); |
| localized_strings->SetString(L"name_input_placeholder", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_NAME_INPUT_PLACE_HOLDER)); |
| localized_strings->SetString(L"url_input_placeholder", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_URL_INPUT_PLACE_HOLDER)); |
| localized_strings->SetString(L"invalid_url", |
| l10n_util::GetString(IDS_BOOKMARK_MANAGER_INVALID_URL)); |
| |
| ChromeURLDataManager::DataSource::SetFontAndTextDirection(localized_strings); |
| |
| result_.reset(localized_strings); |
| SendResponse(true); |
| return true; |
| } |
| |
| bool StartDragBookmarkManagerFunction::RunImpl() { |
| BookmarkModel* model = profile()->GetBookmarkModel(); |
| std::vector<const BookmarkNode*> nodes; |
| EXTENSION_FUNCTION_VALIDATE( |
| GetNodesFromArguments(model, args_as_list(), &nodes)); |
| |
| if (dispatcher()->render_view_host()->delegate()->GetRenderViewType() == |
| ViewType::TAB_CONTENTS) { |
| ExtensionDOMUI* dom_ui = |
| static_cast<ExtensionDOMUI*>(dispatcher()->delegate()); |
| bookmark_utils::DragBookmarks( |
| profile(), nodes, dom_ui->tab_contents()->GetNativeView()); |
| |
| return true; |
| } else { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| bool DropBookmarkManagerFunction::RunImpl() { |
| BookmarkModel* model = profile()->GetBookmarkModel(); |
| |
| // TODO(arv): The arguments change between a list and a value depending on the |
| // parameters. See http://crbug.com/36301 |
| int64 id; |
| std::string id_string; |
| if (args_->IsType(Value::TYPE_STRING)) { |
| EXTENSION_FUNCTION_VALIDATE(args_->GetAsString(&id_string)); |
| } else { |
| EXTENSION_FUNCTION_VALIDATE(args_->IsType(Value::TYPE_LIST)); |
| EXTENSION_FUNCTION_VALIDATE(args_as_list()->GetString(0, &id_string)); |
| } |
| if (!StringToInt64(id_string, &id)) { |
| error_ = keys::kInvalidIdError; |
| return false; |
| } |
| |
| const BookmarkNode* drop_parent = model->GetNodeByID(id); |
| if (!drop_parent) { |
| error_ = keys::kNoParentError; |
| return false; |
| } |
| |
| int drop_index; |
| if (args_as_list()->GetSize() == 2) |
| EXTENSION_FUNCTION_VALIDATE(args_as_list()->GetInteger(1, &drop_index)); |
| else |
| drop_index = drop_parent->GetChildCount(); |
| |
| if (dispatcher()->render_view_host()->delegate()->GetRenderViewType() == |
| ViewType::TAB_CONTENTS) { |
| ExtensionDOMUI* dom_ui = |
| static_cast<ExtensionDOMUI*>(dispatcher()->delegate()); |
| ExtensionBookmarkManagerEventRouter* router = |
| dom_ui->extension_bookmark_manager_event_router(); |
| |
| DCHECK(router); |
| |
| bookmark_utils::PerformBookmarkDrop(profile(), |
| *router->GetBookmarkDragData(), |
| drop_parent, drop_index); |
| |
| router->ClearBookmarkDragData(); |
| SendResponse(true); |
| return true; |
| } else { |
| NOTREACHED(); |
| return false; |
| } |
| } |
| |
| bool GetSubtreeBookmarkManagerFunction::RunImpl() { |
| BookmarkModel* model = profile()->GetBookmarkModel(); |
| const BookmarkNode* node; |
| int64 id; |
| std::string id_string; |
| EXTENSION_FUNCTION_VALIDATE(args_as_list()->GetString(0, &id_string)); |
| bool folders_only; |
| EXTENSION_FUNCTION_VALIDATE(args_as_list()->GetBoolean(1, &folders_only)); |
| if (id_string == "") { |
| node = model->root_node(); |
| } else { |
| if (!StringToInt64(id_string, &id)) { |
| error_ = keys::kInvalidIdError; |
| return false; |
| } |
| node = model->GetNodeByID(id); |
| } |
| if (!node) { |
| error_ = keys::kNoNodeError; |
| return false; |
| } |
| scoped_ptr<ListValue> json(new ListValue()); |
| if (folders_only) { |
| extension_bookmark_helpers::AddNodeFoldersOnly(node, |
| json.get(), |
| true); |
| } else { |
| extension_bookmark_helpers::AddNode(node, json.get(), true); |
| } |
| result_.reset(json.release()); |
| return true; |
| } |