Home | History | Annotate | Download | only in extensions
      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.
     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.
     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.
     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.
    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.
    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 
    144 ExtensionBookmarkManagerEventRouter::ExtensionBookmarkManagerEventRouter(
    145     Profile* profile, TabContents* tab_contents)
    146     : profile_(profile),
    147     tab_contents_(tab_contents) {
    148   tab_contents_->SetBookmarkDragDelegate(this);
    149 }
    150 
    151 ExtensionBookmarkManagerEventRouter::~ExtensionBookmarkManagerEventRouter() {
    152   if (tab_contents_->GetBookmarkDragDelegate() == this)
    153     tab_contents_->SetBookmarkDragDelegate(NULL);
    154 }
    155 
    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 
    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 
    177 void ExtensionBookmarkManagerEventRouter::OnDragEnter(
    178     const BookmarkNodeData& data) {
    179   DispatchDragEvent(data, keys::kOnBookmarkDragEnter);
    180 }
    181 
    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 
    188 void ExtensionBookmarkManagerEventRouter::OnDragLeave(
    189     const BookmarkNodeData& data) {
    190   DispatchDragEvent(data, keys::kOnBookmarkDragLeave);
    191 }
    192 
    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*
    203 ExtensionBookmarkManagerEventRouter::GetBookmarkNodeData() {
    204   if (bookmark_drag_data_.is_valid())
    205     return &bookmark_drag_data_;
    206   return NULL;
    207 }
    208 
    209 void ExtensionBookmarkManagerEventRouter::ClearBookmarkNodeData() {
    210   bookmark_drag_data_.Clear();
    211 }
    212 
    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 
    222 bool CopyBookmarkManagerFunction::RunImpl() {
    223   return CopyOrCut(false);
    224 }
    225 
    226 bool CutBookmarkManagerFunction::RunImpl() {
    227   if (!EditBookmarksEnabled())
    228     return false;
    229   return CopyOrCut(true);
    230 }
    231 
    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 
    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 
    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 
    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 
    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 
    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 
    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