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