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/bookmarks/bookmark_utils.h"
6
7 #include <utility>
8
9 #include "base/basictypes.h"
10 #include "base/file_path.h"
11 #include "base/string16.h"
12 #include "base/string_number_conversions.h"
13 #include "base/time.h"
14 #include "base/utf_string_conversions.h"
15 #include "chrome/browser/bookmarks/bookmark_model.h"
16 #include "chrome/browser/bookmarks/bookmark_node_data.h"
17 #include "chrome/browser/history/query_parser.h"
18 #include "chrome/browser/platform_util.h"
19 #include "chrome/browser/prefs/pref_service.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_list.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/common/pref_names.h"
25 #include "content/browser/tab_contents/page_navigator.h"
26 #include "content/browser/tab_contents/tab_contents.h"
27 #include "content/common/notification_service.h"
28 #include "grit/app_strings.h"
29 #include "grit/chromium_strings.h"
30 #include "grit/generated_resources.h"
31 #include "net/base/net_util.h"
32 #include "ui/base/dragdrop/drag_drop_types.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/base/models/tree_node_iterator.h"
35
36 #if defined(OS_MACOSX)
37 #include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h"
38 #endif
39
40 #if defined(TOOLKIT_VIEWS)
41 #include "ui/base/dragdrop/os_exchange_data.h"
42 #include "views/drag_utils.h"
43 #include "views/events/event.h"
44 #include "views/widget/native_widget.h"
45 #include "views/widget/widget.h"
46 #endif
47
48 #if defined(TOOLKIT_GTK)
49 #include "chrome/browser/ui/gtk/custom_drag.h"
50 #endif
51
52 using base::Time;
53
54 namespace {
55
56 // A PageNavigator implementation that creates a new Browser. This is used when
57 // opening a url and there is no Browser open. The Browser is created the first
58 // time the PageNavigator method is invoked.
59 class NewBrowserPageNavigator : public PageNavigator {
60 public:
NewBrowserPageNavigator(Profile * profile)61 explicit NewBrowserPageNavigator(Profile* profile)
62 : profile_(profile),
63 browser_(NULL) {}
64
~NewBrowserPageNavigator()65 virtual ~NewBrowserPageNavigator() {
66 if (browser_)
67 browser_->window()->Show();
68 }
69
browser() const70 Browser* browser() const { return browser_; }
71
OpenURL(const GURL & url,const GURL & referrer,WindowOpenDisposition disposition,PageTransition::Type transition)72 virtual void OpenURL(const GURL& url,
73 const GURL& referrer,
74 WindowOpenDisposition disposition,
75 PageTransition::Type transition) OVERRIDE {
76 if (!browser_) {
77 Profile* profile = (disposition == OFF_THE_RECORD) ?
78 profile_->GetOffTheRecordProfile() : profile_;
79 browser_ = Browser::Create(profile);
80 // Always open the first tab in the foreground.
81 disposition = NEW_FOREGROUND_TAB;
82 }
83 browser_->OpenURL(url, referrer, NEW_FOREGROUND_TAB, transition);
84 }
85
86 private:
87 Profile* profile_;
88 Browser* browser_;
89
90 DISALLOW_COPY_AND_ASSIGN(NewBrowserPageNavigator);
91 };
92
CloneBookmarkNodeImpl(BookmarkModel * model,const BookmarkNodeData::Element & element,const BookmarkNode * parent,int index_to_add_at)93 void CloneBookmarkNodeImpl(BookmarkModel* model,
94 const BookmarkNodeData::Element& element,
95 const BookmarkNode* parent,
96 int index_to_add_at) {
97 if (element.is_url) {
98 model->AddURL(parent, index_to_add_at, element.title, element.url);
99 } else {
100 const BookmarkNode* new_folder = model->AddFolder(parent,
101 index_to_add_at,
102 element.title);
103 for (int i = 0; i < static_cast<int>(element.children.size()); ++i)
104 CloneBookmarkNodeImpl(model, element.children[i], new_folder, i);
105 }
106 }
107
108 // Returns the number of children of |node| that are of type url.
ChildURLCount(const BookmarkNode * node)109 int ChildURLCount(const BookmarkNode* node) {
110 int result = 0;
111 for (int i = 0; i < node->child_count(); ++i) {
112 const BookmarkNode* child = node->GetChild(i);
113 if (child->is_url())
114 result++;
115 }
116 return result;
117 }
118
119 // Implementation of OpenAll. Opens all nodes of type URL and any children of
120 // |node| that are of type URL. |navigator| is the PageNavigator used to open
121 // URLs. After the first url is opened |opened_url| is set to true and
122 // |navigator| is set to the PageNavigator of the last active tab. This is done
123 // to handle a window disposition of new window, in which case we want
124 // subsequent tabs to open in that window.
OpenAllImpl(const BookmarkNode * node,WindowOpenDisposition initial_disposition,PageNavigator ** navigator,bool * opened_url)125 void OpenAllImpl(const BookmarkNode* node,
126 WindowOpenDisposition initial_disposition,
127 PageNavigator** navigator,
128 bool* opened_url) {
129 if (node->is_url()) {
130 WindowOpenDisposition disposition;
131 if (*opened_url)
132 disposition = NEW_BACKGROUND_TAB;
133 else
134 disposition = initial_disposition;
135 (*navigator)->OpenURL(node->GetURL(), GURL(), disposition,
136 PageTransition::AUTO_BOOKMARK);
137 if (!*opened_url) {
138 *opened_url = true;
139 // We opened the first URL which may have opened a new window or clobbered
140 // the current page, reset the navigator just to be sure.
141 Browser* new_browser = BrowserList::GetLastActive();
142 if (new_browser) {
143 TabContents* current_tab = new_browser->GetSelectedTabContents();
144 DCHECK(new_browser && current_tab);
145 if (new_browser && current_tab)
146 *navigator = current_tab;
147 } // else, new_browser == NULL, which happens during testing.
148 }
149 } else {
150 // For folders only open direct children.
151 for (int i = 0; i < node->child_count(); ++i) {
152 const BookmarkNode* child_node = node->GetChild(i);
153 if (child_node->is_url())
154 OpenAllImpl(child_node, initial_disposition, navigator, opened_url);
155 }
156 }
157 }
158
ShouldOpenAll(gfx::NativeWindow parent,const std::vector<const BookmarkNode * > & nodes)159 bool ShouldOpenAll(gfx::NativeWindow parent,
160 const std::vector<const BookmarkNode*>& nodes) {
161 int child_count = 0;
162 for (size_t i = 0; i < nodes.size(); ++i)
163 child_count += ChildURLCount(nodes[i]);
164 if (child_count < bookmark_utils::num_urls_before_prompting)
165 return true;
166
167 string16 message = l10n_util::GetStringFUTF16(
168 IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL,
169 base::IntToString16(child_count));
170 string16 title = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
171 return platform_util::SimpleYesNoBox(parent, title, message);
172 }
173
174 // Comparison function that compares based on date modified of the two nodes.
MoreRecentlyModified(const BookmarkNode * n1,const BookmarkNode * n2)175 bool MoreRecentlyModified(const BookmarkNode* n1, const BookmarkNode* n2) {
176 return n1->date_folder_modified() > n2->date_folder_modified();
177 }
178
179 // Returns true if |text| contains each string in |words|. This is used when
180 // searching for bookmarks.
DoesBookmarkTextContainWords(const string16 & text,const std::vector<string16> & words)181 bool DoesBookmarkTextContainWords(const string16& text,
182 const std::vector<string16>& words) {
183 for (size_t i = 0; i < words.size(); ++i) {
184 if (text.find(words[i]) == string16::npos)
185 return false;
186 }
187 return true;
188 }
189
190 // Returns true if |node|s title or url contains the strings in |words|.
191 // |languages| argument is user's accept-language setting to decode IDN.
DoesBookmarkContainWords(const BookmarkNode * node,const std::vector<string16> & words,const std::string & languages)192 bool DoesBookmarkContainWords(const BookmarkNode* node,
193 const std::vector<string16>& words,
194 const std::string& languages) {
195 return
196 DoesBookmarkTextContainWords(
197 l10n_util::ToLower(node->GetTitle()), words) ||
198 DoesBookmarkTextContainWords(
199 l10n_util::ToLower(UTF8ToUTF16(node->GetURL().spec())), words) ||
200 DoesBookmarkTextContainWords(l10n_util::ToLower(
201 net::FormatUrl(node->GetURL(), languages, net::kFormatUrlOmitNothing,
202 UnescapeRule::NORMAL, NULL, NULL, NULL)), words);
203 }
204
205 } // namespace
206
207 namespace bookmark_utils {
208
209 int num_urls_before_prompting = 15;
210
PreferredDropOperation(int source_operations,int operations)211 int PreferredDropOperation(int source_operations, int operations) {
212 int common_ops = (source_operations & operations);
213 if (!common_ops)
214 return 0;
215 if (ui::DragDropTypes::DRAG_COPY & common_ops)
216 return ui::DragDropTypes::DRAG_COPY;
217 if (ui::DragDropTypes::DRAG_LINK & common_ops)
218 return ui::DragDropTypes::DRAG_LINK;
219 if (ui::DragDropTypes::DRAG_MOVE & common_ops)
220 return ui::DragDropTypes::DRAG_MOVE;
221 return ui::DragDropTypes::DRAG_NONE;
222 }
223
BookmarkDragOperation(Profile * profile,const BookmarkNode * node)224 int BookmarkDragOperation(Profile* profile, const BookmarkNode* node) {
225 int move = ui::DragDropTypes::DRAG_MOVE;
226 if (!profile->GetPrefs()->GetBoolean(prefs::kEditBookmarksEnabled))
227 move = 0;
228 if (node->is_url()) {
229 return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK | move;
230 }
231 return ui::DragDropTypes::DRAG_COPY | move;
232 }
233
234 #if defined(TOOLKIT_VIEWS)
BookmarkDropOperation(Profile * profile,const views::DropTargetEvent & event,const BookmarkNodeData & data,const BookmarkNode * parent,int index)235 int BookmarkDropOperation(Profile* profile,
236 const views::DropTargetEvent& event,
237 const BookmarkNodeData& data,
238 const BookmarkNode* parent,
239 int index) {
240 if (data.IsFromProfile(profile) && data.size() > 1)
241 // Currently only accept one dragged node at a time.
242 return ui::DragDropTypes::DRAG_NONE;
243
244 if (!bookmark_utils::IsValidDropLocation(profile, data, parent, index))
245 return ui::DragDropTypes::DRAG_NONE;
246
247 if (data.GetFirstNode(profile)) {
248 // User is dragging from this profile: move.
249 return ui::DragDropTypes::DRAG_MOVE;
250 }
251 // User is dragging from another app, copy.
252 return PreferredDropOperation(event.source_operations(),
253 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK);
254 }
255 #endif // defined(TOOLKIT_VIEWS)
256
PerformBookmarkDrop(Profile * profile,const BookmarkNodeData & data,const BookmarkNode * parent_node,int index)257 int PerformBookmarkDrop(Profile* profile,
258 const BookmarkNodeData& data,
259 const BookmarkNode* parent_node,
260 int index) {
261 BookmarkModel* model = profile->GetBookmarkModel();
262 if (data.IsFromProfile(profile)) {
263 const std::vector<const BookmarkNode*> dragged_nodes =
264 data.GetNodes(profile);
265 if (!dragged_nodes.empty()) {
266 // Drag from same profile. Move nodes.
267 for (size_t i = 0; i < dragged_nodes.size(); ++i) {
268 model->Move(dragged_nodes[i], parent_node, index);
269 index = parent_node->GetIndexOf(dragged_nodes[i]) + 1;
270 }
271 return ui::DragDropTypes::DRAG_MOVE;
272 }
273 return ui::DragDropTypes::DRAG_NONE;
274 }
275 // Dropping a folder from different profile. Always accept.
276 bookmark_utils::CloneBookmarkNode(model, data.elements, parent_node, index);
277 return ui::DragDropTypes::DRAG_COPY;
278 }
279
IsValidDropLocation(Profile * profile,const BookmarkNodeData & data,const BookmarkNode * drop_parent,int index)280 bool IsValidDropLocation(Profile* profile,
281 const BookmarkNodeData& data,
282 const BookmarkNode* drop_parent,
283 int index) {
284 if (!drop_parent->is_folder()) {
285 NOTREACHED();
286 return false;
287 }
288
289 if (!data.is_valid())
290 return false;
291
292 if (data.IsFromProfile(profile)) {
293 std::vector<const BookmarkNode*> nodes = data.GetNodes(profile);
294 for (size_t i = 0; i < nodes.size(); ++i) {
295 // Don't allow the drop if the user is attempting to drop on one of the
296 // nodes being dragged.
297 const BookmarkNode* node = nodes[i];
298 int node_index = (drop_parent == node->parent()) ?
299 drop_parent->GetIndexOf(nodes[i]) : -1;
300 if (node_index != -1 && (index == node_index || index == node_index + 1))
301 return false;
302
303 // drop_parent can't accept a child that is an ancestor.
304 if (drop_parent->HasAncestor(node))
305 return false;
306 }
307 return true;
308 }
309 // From the same profile, always accept.
310 return true;
311 }
312
CloneBookmarkNode(BookmarkModel * model,const std::vector<BookmarkNodeData::Element> & elements,const BookmarkNode * parent,int index_to_add_at)313 void CloneBookmarkNode(BookmarkModel* model,
314 const std::vector<BookmarkNodeData::Element>& elements,
315 const BookmarkNode* parent,
316 int index_to_add_at) {
317 if (!parent->is_folder() || !model) {
318 NOTREACHED();
319 return;
320 }
321 for (size_t i = 0; i < elements.size(); ++i)
322 CloneBookmarkNodeImpl(model, elements[i], parent, index_to_add_at + i);
323 }
324
325
326 // Bookmark dragging
DragBookmarks(Profile * profile,const std::vector<const BookmarkNode * > & nodes,gfx::NativeView view)327 void DragBookmarks(Profile* profile,
328 const std::vector<const BookmarkNode*>& nodes,
329 gfx::NativeView view) {
330 DCHECK(!nodes.empty());
331
332 #if defined(TOOLKIT_VIEWS)
333 // Set up our OLE machinery
334 ui::OSExchangeData data;
335 BookmarkNodeData drag_data(nodes);
336 drag_data.Write(profile, &data);
337
338 // Allow nested message loop so we get DnD events as we drag this around.
339 bool was_nested = MessageLoop::current()->IsNested();
340 MessageLoop::current()->SetNestableTasksAllowed(true);
341
342 views::NativeWidget* native_widget =
343 views::NativeWidget::GetNativeWidgetForNativeView(view);
344 if (native_widget) {
345 native_widget->GetWidget()->RunShellDrag(NULL, data,
346 ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE |
347 ui::DragDropTypes::DRAG_LINK);
348 }
349
350 MessageLoop::current()->SetNestableTasksAllowed(was_nested);
351 #elif defined(OS_MACOSX)
352 // Allow nested message loop so we get DnD events as we drag this around.
353 bool was_nested = MessageLoop::current()->IsNested();
354 MessageLoop::current()->SetNestableTasksAllowed(true);
355 bookmark_pasteboard_helper_mac::StartDrag(profile, nodes, view);
356 MessageLoop::current()->SetNestableTasksAllowed(was_nested);
357 #elif defined(TOOLKIT_GTK)
358 BookmarkDrag::BeginDrag(profile, nodes);
359 #endif
360 }
361
OpenAll(gfx::NativeWindow parent,Profile * profile,PageNavigator * navigator,const std::vector<const BookmarkNode * > & nodes,WindowOpenDisposition initial_disposition)362 void OpenAll(gfx::NativeWindow parent,
363 Profile* profile,
364 PageNavigator* navigator,
365 const std::vector<const BookmarkNode*>& nodes,
366 WindowOpenDisposition initial_disposition) {
367 if (!ShouldOpenAll(parent, nodes))
368 return;
369
370 NewBrowserPageNavigator navigator_impl(profile);
371 if (!navigator) {
372 Browser* browser =
373 BrowserList::FindBrowserWithType(profile, Browser::TYPE_NORMAL, false);
374 if (!browser || !browser->GetSelectedTabContents()) {
375 navigator = &navigator_impl;
376 } else {
377 if (initial_disposition != NEW_WINDOW &&
378 initial_disposition != OFF_THE_RECORD) {
379 browser->window()->Activate();
380 }
381 navigator = browser->GetSelectedTabContents();
382 }
383 }
384
385 bool opened_url = false;
386 for (size_t i = 0; i < nodes.size(); ++i)
387 OpenAllImpl(nodes[i], initial_disposition, &navigator, &opened_url);
388 }
389
OpenAll(gfx::NativeWindow parent,Profile * profile,PageNavigator * navigator,const BookmarkNode * node,WindowOpenDisposition initial_disposition)390 void OpenAll(gfx::NativeWindow parent,
391 Profile* profile,
392 PageNavigator* navigator,
393 const BookmarkNode* node,
394 WindowOpenDisposition initial_disposition) {
395 std::vector<const BookmarkNode*> nodes;
396 nodes.push_back(node);
397 OpenAll(parent, profile, navigator, nodes, initial_disposition);
398 }
399
CopyToClipboard(BookmarkModel * model,const std::vector<const BookmarkNode * > & nodes,bool remove_nodes)400 void CopyToClipboard(BookmarkModel* model,
401 const std::vector<const BookmarkNode*>& nodes,
402 bool remove_nodes) {
403 if (nodes.empty())
404 return;
405
406 BookmarkNodeData(nodes).WriteToClipboard(NULL);
407
408 if (remove_nodes) {
409 for (size_t i = 0; i < nodes.size(); ++i) {
410 int index = nodes[i]->parent()->GetIndexOf(nodes[i]);
411 if (index > -1)
412 model->Remove(nodes[i]->parent(), index);
413 }
414 }
415 }
416
PasteFromClipboard(BookmarkModel * model,const BookmarkNode * parent,int index)417 void PasteFromClipboard(BookmarkModel* model,
418 const BookmarkNode* parent,
419 int index) {
420 if (!parent)
421 return;
422
423 BookmarkNodeData bookmark_data;
424 if (!bookmark_data.ReadFromClipboard())
425 return;
426
427 if (index == -1)
428 index = parent->child_count();
429 bookmark_utils::CloneBookmarkNode(
430 model, bookmark_data.elements, parent, index);
431 }
432
CanPasteFromClipboard(const BookmarkNode * node)433 bool CanPasteFromClipboard(const BookmarkNode* node) {
434 if (!node)
435 return false;
436 return BookmarkNodeData::ClipboardContainsBookmarks();
437 }
438
GetNameForURL(const GURL & url)439 string16 GetNameForURL(const GURL& url) {
440 if (url.is_valid()) {
441 return net::GetSuggestedFilename(url, "", "", string16());
442 } else {
443 return l10n_util::GetStringUTF16(IDS_APP_UNTITLED_SHORTCUT_FILE_NAME);
444 }
445 }
446
GetMostRecentlyModifiedFolders(BookmarkModel * model,size_t max_count)447 std::vector<const BookmarkNode*> GetMostRecentlyModifiedFolders(
448 BookmarkModel* model,
449 size_t max_count) {
450 std::vector<const BookmarkNode*> nodes;
451 ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
452 while (iterator.has_next()) {
453 const BookmarkNode* parent = iterator.Next();
454 if (parent->is_folder() && parent->date_folder_modified() > base::Time()) {
455 if (max_count == 0) {
456 nodes.push_back(parent);
457 } else {
458 std::vector<const BookmarkNode*>::iterator i =
459 std::upper_bound(nodes.begin(), nodes.end(), parent,
460 &MoreRecentlyModified);
461 if (nodes.size() < max_count || i != nodes.end()) {
462 nodes.insert(i, parent);
463 while (nodes.size() > max_count)
464 nodes.pop_back();
465 }
466 }
467 } // else case, the root node, which we don't care about or imported nodes
468 // (which have a time of 0).
469 }
470
471 if (nodes.size() < max_count) {
472 // Add the bookmark bar and other nodes if there is space.
473 if (find(nodes.begin(), nodes.end(), model->GetBookmarkBarNode()) ==
474 nodes.end()) {
475 nodes.push_back(model->GetBookmarkBarNode());
476 }
477
478 if (nodes.size() < max_count &&
479 find(nodes.begin(), nodes.end(), model->other_node()) == nodes.end()) {
480 nodes.push_back(model->other_node());
481 }
482 }
483 return nodes;
484 }
485
GetMostRecentlyAddedEntries(BookmarkModel * model,size_t count,std::vector<const BookmarkNode * > * nodes)486 void GetMostRecentlyAddedEntries(BookmarkModel* model,
487 size_t count,
488 std::vector<const BookmarkNode*>* nodes) {
489 ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
490 while (iterator.has_next()) {
491 const BookmarkNode* node = iterator.Next();
492 if (node->is_url()) {
493 std::vector<const BookmarkNode*>::iterator insert_position =
494 std::upper_bound(nodes->begin(), nodes->end(), node,
495 &MoreRecentlyAdded);
496 if (nodes->size() < count || insert_position != nodes->end()) {
497 nodes->insert(insert_position, node);
498 while (nodes->size() > count)
499 nodes->pop_back();
500 }
501 }
502 }
503 }
504
TitleMatch()505 TitleMatch::TitleMatch()
506 : node(NULL) {
507 }
508
~TitleMatch()509 TitleMatch::~TitleMatch() {}
510
MoreRecentlyAdded(const BookmarkNode * n1,const BookmarkNode * n2)511 bool MoreRecentlyAdded(const BookmarkNode* n1, const BookmarkNode* n2) {
512 return n1->date_added() > n2->date_added();
513 }
514
GetBookmarksContainingText(BookmarkModel * model,const string16 & text,size_t max_count,const std::string & languages,std::vector<const BookmarkNode * > * nodes)515 void GetBookmarksContainingText(BookmarkModel* model,
516 const string16& text,
517 size_t max_count,
518 const std::string& languages,
519 std::vector<const BookmarkNode*>* nodes) {
520 std::vector<string16> words;
521 QueryParser parser;
522 parser.ExtractQueryWords(l10n_util::ToLower(text), &words);
523 if (words.empty())
524 return;
525
526 ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node());
527 while (iterator.has_next()) {
528 const BookmarkNode* node = iterator.Next();
529 if (node->is_url() && DoesBookmarkContainWords(node, words, languages)) {
530 nodes->push_back(node);
531 if (nodes->size() == max_count)
532 return;
533 }
534 }
535 }
536
DoesBookmarkContainText(const BookmarkNode * node,const string16 & text,const std::string & languages)537 bool DoesBookmarkContainText(const BookmarkNode* node,
538 const string16& text,
539 const std::string& languages) {
540 std::vector<string16> words;
541 QueryParser parser;
542 parser.ExtractQueryWords(l10n_util::ToLower(text), &words);
543 if (words.empty())
544 return false;
545
546 return (node->is_url() && DoesBookmarkContainWords(node, words, languages));
547 }
548
CreateNewNode(BookmarkModel * model,const BookmarkNode * parent,const BookmarkEditor::EditDetails & details,const string16 & new_title,const GURL & new_url)549 static const BookmarkNode* CreateNewNode(BookmarkModel* model,
550 const BookmarkNode* parent, const BookmarkEditor::EditDetails& details,
551 const string16& new_title, const GURL& new_url) {
552 const BookmarkNode* node;
553 if (details.type == BookmarkEditor::EditDetails::NEW_URL) {
554 node = model->AddURL(parent, parent->child_count(), new_title, new_url);
555 } else if (details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
556 node = model->AddFolder(parent, parent->child_count(), new_title);
557 for (size_t i = 0; i < details.urls.size(); ++i) {
558 model->AddURL(node, node->child_count(), details.urls[i].second,
559 details.urls[i].first);
560 }
561 model->SetDateFolderModified(parent, Time::Now());
562 } else {
563 NOTREACHED();
564 return NULL;
565 }
566
567 return node;
568 }
569
ApplyEditsWithNoFolderChange(BookmarkModel * model,const BookmarkNode * parent,const BookmarkEditor::EditDetails & details,const string16 & new_title,const GURL & new_url)570 const BookmarkNode* ApplyEditsWithNoFolderChange(BookmarkModel* model,
571 const BookmarkNode* parent, const BookmarkEditor::EditDetails& details,
572 const string16& new_title, const GURL& new_url) {
573 if (details.type == BookmarkEditor::EditDetails::NEW_URL ||
574 details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
575 return CreateNewNode(model, parent, details, new_title, new_url);
576 }
577
578 const BookmarkNode* node = details.existing_node;
579 DCHECK(node);
580
581 if (node->is_url())
582 model->SetURL(node, new_url);
583 model->SetTitle(node, new_title);
584
585 return node;
586 }
587
ApplyEditsWithPossibleFolderChange(BookmarkModel * model,const BookmarkNode * new_parent,const BookmarkEditor::EditDetails & details,const string16 & new_title,const GURL & new_url)588 const BookmarkNode* ApplyEditsWithPossibleFolderChange(BookmarkModel* model,
589 const BookmarkNode* new_parent, const BookmarkEditor::EditDetails& details,
590 const string16& new_title, const GURL& new_url) {
591 if (details.type == BookmarkEditor::EditDetails::NEW_URL ||
592 details.type == BookmarkEditor::EditDetails::NEW_FOLDER) {
593 return CreateNewNode(model, new_parent, details, new_title, new_url);
594 }
595
596 const BookmarkNode* node = details.existing_node;
597 DCHECK(node);
598
599 if (new_parent != node->parent())
600 model->Move(node, new_parent, new_parent->child_count());
601 if (node->is_url())
602 model->SetURL(node, new_url);
603 model->SetTitle(node, new_title);
604
605 return node;
606 }
607
608 // Formerly in BookmarkBarView
ToggleWhenVisible(Profile * profile)609 void ToggleWhenVisible(Profile* profile) {
610 PrefService* prefs = profile->GetPrefs();
611 const bool always_show = !prefs->GetBoolean(prefs::kShowBookmarkBar);
612
613 // The user changed when the bookmark bar is shown, update the preferences.
614 prefs->SetBoolean(prefs::kShowBookmarkBar, always_show);
615 prefs->ScheduleSavePersistentPrefs();
616
617 // And notify the notification service.
618 Source<Profile> source(profile);
619 NotificationService::current()->Notify(
620 NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
621 source,
622 NotificationService::NoDetails());
623 }
624
RegisterUserPrefs(PrefService * prefs)625 void RegisterUserPrefs(PrefService* prefs) {
626 prefs->RegisterBooleanPref(prefs::kShowBookmarkBar, false);
627 prefs->RegisterBooleanPref(prefs::kEditBookmarksEnabled, true);
628 }
629
GetURLAndTitleToBookmark(TabContents * tab_contents,GURL * url,string16 * title)630 void GetURLAndTitleToBookmark(TabContents* tab_contents,
631 GURL* url,
632 string16* title) {
633 *url = tab_contents->GetURL();
634 *title = tab_contents->GetTitle();
635 }
636
GetURLsForOpenTabs(Browser * browser,std::vector<std::pair<GURL,string16>> * urls)637 void GetURLsForOpenTabs(Browser* browser,
638 std::vector<std::pair<GURL, string16> >* urls) {
639 for (int i = 0; i < browser->tab_count(); ++i) {
640 std::pair<GURL, string16> entry;
641 GetURLAndTitleToBookmark(browser->GetTabContentsAt(i), &(entry.first),
642 &(entry.second));
643 urls->push_back(entry);
644 }
645 }
646
GetParentForNewNodes(const BookmarkNode * parent,const std::vector<const BookmarkNode * > & selection,int * index)647 const BookmarkNode* GetParentForNewNodes(
648 const BookmarkNode* parent,
649 const std::vector<const BookmarkNode*>& selection,
650 int* index) {
651 const BookmarkNode* real_parent = parent;
652
653 if (selection.size() == 1 && selection[0]->is_folder())
654 real_parent = selection[0];
655
656 if (index) {
657 if (selection.size() == 1 && selection[0]->is_url()) {
658 *index = real_parent->GetIndexOf(selection[0]) + 1;
659 if (*index == 0) {
660 // Node doesn't exist in parent, add to end.
661 NOTREACHED();
662 *index = real_parent->child_count();
663 }
664 } else {
665 *index = real_parent->child_count();
666 }
667 }
668
669 return real_parent;
670 }
671
NodeHasURLs(const BookmarkNode * node)672 bool NodeHasURLs(const BookmarkNode* node) {
673 DCHECK(node);
674
675 if (node->is_url())
676 return true;
677
678 for (int i = 0; i < node->child_count(); ++i) {
679 if (NodeHasURLs(node->GetChild(i)))
680 return true;
681 }
682 return false;
683 }
684
685 } // namespace bookmark_utils
686