• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/ui/views/bookmarks/bookmark_editor_view.h"
6 
7 #include "base/basictypes.h"
8 #include "base/logging.h"
9 #include "base/string_util.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/bookmarks/bookmark_model.h"
12 #include "chrome/browser/bookmarks/bookmark_utils.h"
13 #include "chrome/browser/history/history.h"
14 #include "chrome/browser/net/url_fixer_upper.h"
15 #include "chrome/browser/prefs/pref_service.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/common/pref_names.h"
18 #include "googleurl/src/gurl.h"
19 #include "grit/chromium_strings.h"
20 #include "grit/generated_resources.h"
21 #include "grit/locale_settings.h"
22 #include "net/base/net_util.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "views/background.h"
25 #include "views/controls/button/native_button.h"
26 #include "views/controls/label.h"
27 #include "views/controls/menu/menu_2.h"
28 #include "views/controls/textfield/textfield.h"
29 #include "views/focus/focus_manager.h"
30 #include "views/layout/grid_layout.h"
31 #include "views/layout/layout_constants.h"
32 #include "views/widget/widget.h"
33 #include "views/window/window.h"
34 
35 using views::Button;
36 using views::ColumnSet;
37 using views::GridLayout;
38 using views::Label;
39 using views::NativeButton;
40 using views::Textfield;
41 
42 // Background color of text field when URL is invalid.
43 static const SkColor kErrorColor = SkColorSetRGB(0xFF, 0xBC, 0xBC);
44 
45 // Preferred width of the tree.
46 static const int kTreeWidth = 300;
47 
48 // ID for various children.
49 static const int kNewFolderButtonID = 1002;
50 
51 // static
Show(HWND parent_hwnd,Profile * profile,const BookmarkNode * parent,const EditDetails & details,Configuration configuration)52 void BookmarkEditor::Show(HWND parent_hwnd,
53                           Profile* profile,
54                           const BookmarkNode* parent,
55                           const EditDetails& details,
56                           Configuration configuration) {
57   DCHECK(profile);
58   BookmarkEditorView* editor =
59       new BookmarkEditorView(profile, parent, details, configuration);
60   editor->Show(parent_hwnd);
61 }
62 
BookmarkEditorView(Profile * profile,const BookmarkNode * parent,const EditDetails & details,BookmarkEditor::Configuration configuration)63 BookmarkEditorView::BookmarkEditorView(
64     Profile* profile,
65     const BookmarkNode* parent,
66     const EditDetails& details,
67     BookmarkEditor::Configuration configuration)
68     : profile_(profile),
69       tree_view_(NULL),
70       new_folder_button_(NULL),
71       url_label_(NULL),
72       title_label_(NULL),
73       parent_(parent),
74       details_(details),
75       running_menu_for_root_(false),
76       show_tree_(configuration == SHOW_TREE) {
77   DCHECK(profile);
78   Init();
79 }
80 
~BookmarkEditorView()81 BookmarkEditorView::~BookmarkEditorView() {
82   // The tree model is deleted before the view. Reset the model otherwise the
83   // tree will reference a deleted model.
84   if (tree_view_)
85     tree_view_->SetModel(NULL);
86   bb_model_->RemoveObserver(this);
87 }
88 
IsDialogButtonEnabled(MessageBoxFlags::DialogButton button) const89 bool BookmarkEditorView::IsDialogButtonEnabled(
90     MessageBoxFlags::DialogButton button) const {
91   if (button == MessageBoxFlags::DIALOGBUTTON_OK) {
92     if (details_.type == EditDetails::NEW_FOLDER)
93       return !title_tf_.text().empty();
94 
95     const GURL url(GetInputURL());
96     return bb_model_->IsLoaded() && url.is_valid();
97   }
98   return true;
99 }
100 
IsModal() const101 bool BookmarkEditorView::IsModal() const {
102   return true;
103 }
104 
CanResize() const105 bool BookmarkEditorView::CanResize() const {
106   return true;
107 }
108 
GetWindowTitle() const109 std::wstring BookmarkEditorView::GetWindowTitle() const {
110   return UTF16ToWide(l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_TITLE));
111 }
112 
Accept()113 bool BookmarkEditorView::Accept() {
114   if (!IsDialogButtonEnabled(MessageBoxFlags::DIALOGBUTTON_OK)) {
115     // The url is invalid, focus the url field.
116     url_tf_.SelectAll();
117     url_tf_.RequestFocus();
118     return false;
119   }
120   // Otherwise save changes and close the dialog box.
121   ApplyEdits();
122   return true;
123 }
124 
AreAcceleratorsEnabled(MessageBoxFlags::DialogButton button)125 bool BookmarkEditorView::AreAcceleratorsEnabled(
126     MessageBoxFlags::DialogButton button) {
127   return !show_tree_ || !tree_view_->GetEditingNode();
128 }
129 
GetContentsView()130 views::View* BookmarkEditorView::GetContentsView() {
131   return this;
132 }
133 
Layout()134 void BookmarkEditorView::Layout() {
135   // Let the grid layout manager lay out most of the dialog...
136   GetLayoutManager()->Layout(this);
137 
138   if (!show_tree_)
139     return;
140 
141   // Manually lay out the New Folder button in the same row as the OK/Cancel
142   // buttons...
143   gfx::Rect parent_bounds = parent()->GetContentsBounds();
144   gfx::Size prefsize = new_folder_button_->GetPreferredSize();
145   int button_y =
146       parent_bounds.bottom() - prefsize.height() - views::kButtonVEdgeMargin;
147   new_folder_button_->SetBounds(
148       views::kPanelHorizMargin, button_y, prefsize.width(), prefsize.height());
149 }
150 
GetPreferredSize()151 gfx::Size BookmarkEditorView::GetPreferredSize() {
152   if (!show_tree_)
153     return views::View::GetPreferredSize();
154 
155   return gfx::Size(views::Window::GetLocalizedContentsSize(
156       IDS_EDITBOOKMARK_DIALOG_WIDTH_CHARS,
157       IDS_EDITBOOKMARK_DIALOG_HEIGHT_LINES));
158 }
159 
ViewHierarchyChanged(bool is_add,views::View * parent,views::View * child)160 void BookmarkEditorView::ViewHierarchyChanged(bool is_add,
161                                               views::View* parent,
162                                               views::View* child) {
163   if (show_tree_ && child == this) {
164     // Add and remove the New Folder button from the ClientView's hierarchy.
165     if (is_add) {
166       parent->AddChildView(new_folder_button_.get());
167     } else {
168       parent->RemoveChildView(new_folder_button_.get());
169     }
170   }
171 }
172 
OnTreeViewSelectionChanged(views::TreeView * tree_view)173 void BookmarkEditorView::OnTreeViewSelectionChanged(
174     views::TreeView* tree_view) {
175 }
176 
CanEdit(views::TreeView * tree_view,ui::TreeModelNode * node)177 bool BookmarkEditorView::CanEdit(views::TreeView* tree_view,
178                                  ui::TreeModelNode* node) {
179   // Only allow editting of children of the bookmark bar node and other node.
180   EditorNode* bb_node = tree_model_->AsNode(node);
181   return (bb_node->parent() && bb_node->parent()->parent());
182 }
183 
ContentsChanged(Textfield * sender,const std::wstring & new_contents)184 void BookmarkEditorView::ContentsChanged(Textfield* sender,
185                                          const std::wstring& new_contents) {
186   UserInputChanged();
187 }
188 
ButtonPressed(Button * sender,const views::Event & event)189 void BookmarkEditorView::ButtonPressed(
190     Button* sender, const views::Event& event) {
191   DCHECK(sender);
192   switch (sender->GetID()) {
193     case kNewFolderButtonID:
194       NewFolder();
195       break;
196 
197     default:
198       NOTREACHED();
199   }
200 }
201 
IsCommandIdChecked(int command_id) const202 bool BookmarkEditorView::IsCommandIdChecked(int command_id) const {
203   return false;
204 }
205 
IsCommandIdEnabled(int command_id) const206 bool BookmarkEditorView::IsCommandIdEnabled(int command_id) const {
207   return (command_id != IDS_EDIT || !running_menu_for_root_);
208 }
209 
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)210 bool BookmarkEditorView::GetAcceleratorForCommandId(
211     int command_id,
212     ui::Accelerator* accelerator) {
213   return GetWidget()->GetAccelerator(command_id, accelerator);
214 }
215 
ExecuteCommand(int command_id)216 void BookmarkEditorView::ExecuteCommand(int command_id) {
217   DCHECK(tree_view_->GetSelectedNode());
218   if (command_id == IDS_EDIT) {
219     tree_view_->StartEditing(tree_view_->GetSelectedNode());
220   } else {
221     DCHECK(command_id == IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM);
222     NewFolder();
223   }
224 }
225 
Show(HWND parent_hwnd)226 void BookmarkEditorView::Show(HWND parent_hwnd) {
227   views::Window::CreateChromeWindow(parent_hwnd, gfx::Rect(), this);
228   UserInputChanged();
229   if (show_tree_ && bb_model_->IsLoaded())
230     ExpandAndSelect();
231   window()->Show();
232   // Select all the text in the name Textfield.
233   title_tf_.SelectAll();
234   // Give focus to the name Textfield.
235   title_tf_.RequestFocus();
236 }
237 
Close()238 void BookmarkEditorView::Close() {
239   DCHECK(window());
240   window()->CloseWindow();
241 }
242 
ShowContextMenuForView(View * source,const gfx::Point & p,bool is_mouse_gesture)243 void BookmarkEditorView::ShowContextMenuForView(View* source,
244                                                 const gfx::Point& p,
245                                                 bool is_mouse_gesture) {
246   DCHECK(source == tree_view_);
247   if (!tree_view_->GetSelectedNode())
248     return;
249   running_menu_for_root_ =
250       (tree_model_->GetParent(tree_view_->GetSelectedNode()) ==
251        tree_model_->GetRoot());
252   if (!context_menu_contents_.get()) {
253     context_menu_contents_.reset(new ui::SimpleMenuModel(this));
254     context_menu_contents_->AddItemWithStringId(IDS_EDIT, IDS_EDIT);
255     context_menu_contents_->AddItemWithStringId(
256         IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM,
257         IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM);
258     context_menu_.reset(new views::Menu2(context_menu_contents_.get()));
259   }
260   context_menu_->RunContextMenuAt(p);
261 }
262 
Init()263 void BookmarkEditorView::Init() {
264   bb_model_ = profile_->GetBookmarkModel();
265   DCHECK(bb_model_);
266   bb_model_->AddObserver(this);
267 
268   url_tf_.set_parent_owned(false);
269   title_tf_.set_parent_owned(false);
270 
271   std::wstring title;
272   if (details_.type == EditDetails::EXISTING_NODE)
273     title = details_.existing_node->GetTitle();
274   else if (details_.type == EditDetails::NEW_FOLDER)
275     title = UTF16ToWide(
276         l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME));
277   title_tf_.SetText(title);
278   title_tf_.SetController(this);
279 
280   title_label_ = new views::Label(
281       UTF16ToWide(l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_NAME_LABEL)));
282   title_tf_.SetAccessibleName(WideToUTF16Hack(title_label_->GetText()));
283 
284   string16 url_text;
285   if (details_.type == EditDetails::EXISTING_NODE) {
286     std::string languages = profile_
287         ? profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)
288         : std::string();
289     // Because this gets parsed by FixupURL(), it's safe to omit the scheme or
290     // trailing slash, and unescape most characters, but we need to not drop any
291     // username/password, or unescape anything that changes the meaning.
292     url_text = net::FormatUrl(details_.existing_node->GetURL(), languages,
293         net::kFormatUrlOmitAll & ~net::kFormatUrlOmitUsernamePassword,
294         UnescapeRule::SPACES, NULL, NULL, NULL);
295   }
296   url_tf_.SetText(UTF16ToWide(url_text));
297   url_tf_.SetController(this);
298 
299   url_label_ = new views::Label(
300       UTF16ToWide(l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_URL_LABEL)));
301   url_tf_.SetAccessibleName(WideToUTF16Hack(url_label_->GetText()));
302 
303   if (show_tree_) {
304     tree_view_ = new views::TreeView();
305     new_folder_button_.reset(new views::NativeButton(
306         this,
307         UTF16ToWide(l10n_util::GetStringUTF16(
308             IDS_BOOMARK_EDITOR_NEW_FOLDER_BUTTON))));
309     new_folder_button_->set_parent_owned(false);
310     tree_view_->SetContextMenuController(this);
311 
312     tree_view_->SetRootShown(false);
313     new_folder_button_->SetEnabled(false);
314     new_folder_button_->SetID(kNewFolderButtonID);
315   }
316 
317   // Yummy layout code.
318   GridLayout* layout = GridLayout::CreatePanel(this);
319   SetLayoutManager(layout);
320 
321   const int labels_column_set_id = 0;
322   const int single_column_view_set_id = 1;
323   const int buttons_column_set_id = 2;
324 
325   ColumnSet* column_set = layout->AddColumnSet(labels_column_set_id);
326   column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
327                         GridLayout::USE_PREF, 0, 0);
328   column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
329   column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
330                         GridLayout::USE_PREF, 0, 0);
331 
332   column_set = layout->AddColumnSet(single_column_view_set_id);
333   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
334                         GridLayout::FIXED, kTreeWidth, 0);
335 
336   column_set = layout->AddColumnSet(buttons_column_set_id);
337   column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
338                         GridLayout::USE_PREF, 0, 0);
339   column_set->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing);
340   column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
341                         GridLayout::USE_PREF, 0, 0);
342   column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
343   column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
344                         GridLayout::USE_PREF, 0, 0);
345   column_set->LinkColumnSizes(0, 2, 4, -1);
346 
347   layout->StartRow(0, labels_column_set_id);
348 
349   layout->AddView(title_label_);
350   layout->AddView(&title_tf_);
351 
352   if (details_.type != EditDetails::NEW_FOLDER) {
353     layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
354 
355     layout->StartRow(0, labels_column_set_id);
356     layout->AddView(url_label_);
357     layout->AddView(&url_tf_);
358   }
359 
360   if (show_tree_) {
361     layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
362     layout->StartRow(1, single_column_view_set_id);
363     layout->AddView(tree_view_);
364   }
365 
366   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
367 
368   if (!show_tree_ || bb_model_->IsLoaded())
369     Reset();
370 }
371 
BookmarkNodeMoved(BookmarkModel * model,const BookmarkNode * old_parent,int old_index,const BookmarkNode * new_parent,int new_index)372 void BookmarkEditorView::BookmarkNodeMoved(BookmarkModel* model,
373                                            const BookmarkNode* old_parent,
374                                            int old_index,
375                                            const BookmarkNode* new_parent,
376                                            int new_index) {
377   Reset();
378 }
379 
BookmarkNodeAdded(BookmarkModel * model,const BookmarkNode * parent,int index)380 void BookmarkEditorView::BookmarkNodeAdded(BookmarkModel* model,
381                                            const BookmarkNode* parent,
382                                            int index) {
383   Reset();
384 }
385 
BookmarkNodeRemoved(BookmarkModel * model,const BookmarkNode * parent,int index,const BookmarkNode * node)386 void BookmarkEditorView::BookmarkNodeRemoved(BookmarkModel* model,
387                                              const BookmarkNode* parent,
388                                              int index,
389                                              const BookmarkNode* node) {
390   if ((details_.type == EditDetails::EXISTING_NODE &&
391        details_.existing_node->HasAncestor(node)) ||
392       (parent_ && parent_->HasAncestor(node))) {
393     // The node, or its parent was removed. Close the dialog.
394     window()->CloseWindow();
395   } else {
396     Reset();
397   }
398 }
399 
BookmarkNodeChildrenReordered(BookmarkModel * model,const BookmarkNode * node)400 void BookmarkEditorView::BookmarkNodeChildrenReordered(
401     BookmarkModel* model, const BookmarkNode* node) {
402   Reset();
403 }
404 
Reset()405 void BookmarkEditorView::Reset() {
406   if (!show_tree_) {
407     if (parent())
408       UserInputChanged();
409     return;
410   }
411 
412   new_folder_button_->SetEnabled(true);
413 
414   // Do this first, otherwise when we invoke SetModel with the real one
415   // tree_view will try to invoke something on the model we just deleted.
416   tree_view_->SetModel(NULL);
417 
418   EditorNode* root_node = CreateRootNode();
419   tree_model_.reset(new EditorTreeModel(root_node));
420 
421   tree_view_->SetModel(tree_model_.get());
422   tree_view_->SetController(this);
423 
424   context_menu_.reset();
425 
426   if (parent())
427     ExpandAndSelect();
428 }
GetInputURL() const429 GURL BookmarkEditorView::GetInputURL() const {
430   return URLFixerUpper::FixupURL(UTF16ToUTF8(url_tf_.text()), std::string());
431 }
432 
GetInputTitle() const433 std::wstring BookmarkEditorView::GetInputTitle() const {
434   return title_tf_.text();
435 }
436 
UserInputChanged()437 void BookmarkEditorView::UserInputChanged() {
438   const GURL url(GetInputURL());
439   if (!url.is_valid())
440     url_tf_.SetBackgroundColor(kErrorColor);
441   else
442     url_tf_.UseDefaultBackgroundColor();
443   GetDialogClientView()->UpdateDialogButtons();
444 }
445 
NewFolder()446 void BookmarkEditorView::NewFolder() {
447   // Create a new entry parented to the selected item, or the bookmark
448   // bar if nothing is selected.
449   EditorNode* parent = tree_model_->AsNode(tree_view_->GetSelectedNode());
450   if (!parent) {
451     NOTREACHED();
452     return;
453   }
454 
455   tree_view_->StartEditing(AddNewFolder(parent));
456 }
457 
AddNewFolder(EditorNode * parent)458 BookmarkEditorView::EditorNode* BookmarkEditorView::AddNewFolder(
459     EditorNode* parent) {
460   EditorNode* new_node = new EditorNode();
461   new_node->set_title(
462       l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME));
463   new_node->value = 0;
464   // new_node is now owned by parent.
465   tree_model_->Add(parent, new_node, parent->child_count());
466   return new_node;
467 }
468 
ExpandAndSelect()469 void BookmarkEditorView::ExpandAndSelect() {
470   tree_view_->ExpandAll();
471 
472   const BookmarkNode* to_select = parent_;
473   if (details_.type == EditDetails::EXISTING_NODE)
474     to_select = details_.existing_node->parent();
475   int64 folder_id_to_select = to_select->id();
476   EditorNode* b_node =
477       FindNodeWithID(tree_model_->GetRoot(), folder_id_to_select);
478   if (!b_node)
479     b_node = tree_model_->GetRoot()->GetChild(0);  // Bookmark bar node.
480 
481   tree_view_->SetSelectedNode(b_node);
482 }
483 
CreateRootNode()484 BookmarkEditorView::EditorNode* BookmarkEditorView::CreateRootNode() {
485   EditorNode* root_node = new EditorNode(std::wstring(), 0);
486   const BookmarkNode* bb_root_node = bb_model_->root_node();
487   CreateNodes(bb_root_node, root_node);
488   DCHECK(root_node->child_count() == 2);
489   DCHECK(bb_root_node->GetChild(0)->type() == BookmarkNode::BOOKMARK_BAR);
490   DCHECK(bb_root_node->GetChild(1)->type() == BookmarkNode::OTHER_NODE);
491   return root_node;
492 }
493 
CreateNodes(const BookmarkNode * bb_node,BookmarkEditorView::EditorNode * b_node)494 void BookmarkEditorView::CreateNodes(const BookmarkNode* bb_node,
495                                      BookmarkEditorView::EditorNode* b_node) {
496   for (int i = 0; i < bb_node->child_count(); ++i) {
497     const BookmarkNode* child_bb_node = bb_node->GetChild(i);
498     if (child_bb_node->is_folder()) {
499       EditorNode* new_b_node =
500           new EditorNode(WideToUTF16(child_bb_node->GetTitle()),
501                                      child_bb_node->id());
502       b_node->Add(new_b_node, b_node->child_count());
503       CreateNodes(child_bb_node, new_b_node);
504     }
505   }
506 }
507 
FindNodeWithID(BookmarkEditorView::EditorNode * node,int64 id)508 BookmarkEditorView::EditorNode* BookmarkEditorView::FindNodeWithID(
509     BookmarkEditorView::EditorNode* node,
510     int64 id) {
511   if (node->value == id)
512     return node;
513   for (int i = 0; i < node->child_count(); ++i) {
514     EditorNode* result = FindNodeWithID(node->GetChild(i), id);
515     if (result)
516       return result;
517   }
518   return NULL;
519 }
520 
ApplyEdits()521 void BookmarkEditorView::ApplyEdits() {
522   DCHECK(bb_model_->IsLoaded());
523 
524   EditorNode* parent = show_tree_ ?
525       tree_model_->AsNode(tree_view_->GetSelectedNode()) : NULL;
526   if (show_tree_ && !parent) {
527     NOTREACHED();
528     return;
529   }
530   ApplyEdits(parent);
531 }
532 
ApplyEdits(EditorNode * parent)533 void BookmarkEditorView::ApplyEdits(EditorNode* parent) {
534   DCHECK(!show_tree_ || parent);
535 
536   // We're going to apply edits to the bookmark bar model, which will call us
537   // back. Normally when a structural edit occurs we reset the tree model.
538   // We don't want to do that here, so we remove ourselves as an observer.
539   bb_model_->RemoveObserver(this);
540 
541   GURL new_url(GetInputURL());
542   string16 new_title(WideToUTF16Hack(GetInputTitle()));
543 
544   if (!show_tree_) {
545     bookmark_utils::ApplyEditsWithNoFolderChange(
546         bb_model_, parent_, details_, new_title, new_url);
547     return;
548   }
549 
550   // Create the new folders and update the titles.
551   const BookmarkNode* new_parent = NULL;
552   ApplyNameChangesAndCreateNewFolders(
553       bb_model_->root_node(), tree_model_->GetRoot(), parent, &new_parent);
554 
555   bookmark_utils::ApplyEditsWithPossibleFolderChange(
556       bb_model_, new_parent, details_, new_title, new_url);
557 }
558 
ApplyNameChangesAndCreateNewFolders(const BookmarkNode * bb_node,BookmarkEditorView::EditorNode * b_node,BookmarkEditorView::EditorNode * parent_b_node,const BookmarkNode ** parent_bb_node)559 void BookmarkEditorView::ApplyNameChangesAndCreateNewFolders(
560     const BookmarkNode* bb_node,
561     BookmarkEditorView::EditorNode* b_node,
562     BookmarkEditorView::EditorNode* parent_b_node,
563     const BookmarkNode** parent_bb_node) {
564   if (parent_b_node == b_node)
565     *parent_bb_node = bb_node;
566   for (int i = 0; i < b_node->child_count(); ++i) {
567     EditorNode* child_b_node = b_node->GetChild(i);
568     const BookmarkNode* child_bb_node = NULL;
569     if (child_b_node->value == 0) {
570       // New folder.
571       child_bb_node = bb_model_->AddFolder(bb_node,
572           bb_node->child_count(), child_b_node->GetTitle());
573     } else {
574       // Existing node, reset the title (BookmarkModel ignores changes if the
575       // title is the same).
576       for (int j = 0; j < bb_node->child_count(); ++j) {
577         const BookmarkNode* node = bb_node->GetChild(j);
578         if (node->is_folder() && node->id() == child_b_node->value) {
579           child_bb_node = node;
580           break;
581         }
582       }
583       DCHECK(child_bb_node);
584       bb_model_->SetTitle(child_bb_node, child_b_node->GetTitle());
585     }
586     ApplyNameChangesAndCreateNewFolders(child_bb_node, child_b_node,
587                                         parent_b_node, parent_bb_node);
588   }
589 }
590