OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/sync/glue/bookmark_model_associator.h" | 5 #include "chrome/browser/sync/glue/bookmark_model_associator.h" |
6 | 6 |
7 #include <stack> | 7 #include <stack> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
59 // TODO(ncarter): Pull these tags from an external protocol specification | 59 // TODO(ncarter): Pull these tags from an external protocol specification |
60 // rather than hardcoding them here. | 60 // rather than hardcoding them here. |
61 const char kBookmarkBarTag[] = "bookmark_bar"; | 61 const char kBookmarkBarTag[] = "bookmark_bar"; |
62 const char kMobileBookmarksTag[] = "synced_bookmarks"; | 62 const char kMobileBookmarksTag[] = "synced_bookmarks"; |
63 const char kOtherBookmarksTag[] = "other_bookmarks"; | 63 const char kOtherBookmarksTag[] = "other_bookmarks"; |
64 | 64 |
65 // Maximum number of bytes to allow in a title (must match sync's internal | 65 // Maximum number of bytes to allow in a title (must match sync's internal |
66 // limits; see write_node.cc). | 66 // limits; see write_node.cc). |
67 const int kTitleLimitBytes = 255; | 67 const int kTitleLimitBytes = 255; |
68 | 68 |
69 // Bookmark comparer for map of bookmark nodes. | |
70 class BookmarkComparer { | |
71 public: | |
72 // Compares the two given nodes and returns whether node1 should appear | |
73 // before node2 in strict weak ordering. | |
74 bool operator()(const BookmarkNode* node1, | |
75 const BookmarkNode* node2) const { | |
76 DCHECK(node1); | |
77 DCHECK(node2); | |
78 | |
79 // Keep folder nodes before non-folder nodes. | |
80 if (node1->is_folder() != node2->is_folder()) | |
81 return node1->is_folder(); | |
82 | |
83 // Truncate bookmark titles in the form sync does internally to avoid | |
84 // mismatches due to sync munging titles. | |
85 std::string title1 = base::UTF16ToUTF8(node1->GetTitle()); | |
86 syncer::SyncAPINameToServerName(title1, &title1); | |
87 base::TruncateUTF8ToByteSize(title1, kTitleLimitBytes, &title1); | |
88 | |
89 std::string title2 = base::UTF16ToUTF8(node2->GetTitle()); | |
90 syncer::SyncAPINameToServerName(title2, &title2); | |
91 base::TruncateUTF8ToByteSize(title2, kTitleLimitBytes, &title2); | |
92 | |
93 int result = title1.compare(title2); | |
94 if (result != 0) | |
95 return result < 0; | |
96 | |
97 return node1->url() < node2->url(); | |
98 } | |
99 }; | |
100 | |
101 // Provides the following abstraction: given a parent bookmark node, find best | 69 // Provides the following abstraction: given a parent bookmark node, find best |
102 // matching child node for many sync nodes. | 70 // matching child node for many sync nodes. |
103 class BookmarkNodeFinder { | 71 class BookmarkNodeFinder { |
104 public: | 72 public: |
105 // Creates an instance with the given parent bookmark node. | 73 // Creates an instance with the given parent bookmark node. |
106 explicit BookmarkNodeFinder(const BookmarkNode* parent_node); | 74 explicit BookmarkNodeFinder(const BookmarkNode* parent_node); |
107 | 75 |
108 // Finds the bookmark node that matches the given url, title and folder | 76 // Finds the bookmark node that matches the given url, title and folder |
109 // attribute. Returns the matching node if one exists; NULL otherwise. If a | 77 // attribute. Returns the matching node if one exists; NULL otherwise. If a |
110 // matching node is found, it's removed for further matches. | 78 // matching node is found, it's removed for further matches. |
111 const BookmarkNode* FindBookmarkNode(const GURL& url, | 79 const BookmarkNode* FindBookmarkNode(const GURL& url, |
112 const std::string& title, | 80 const std::string& title, |
113 bool is_folder); | 81 bool is_folder); |
114 | 82 |
115 private: | 83 private: |
116 typedef std::multiset<const BookmarkNode*, BookmarkComparer> BookmarkNodesSet; | 84 // Maps bookmark node titles to instances, duplicates allowed. |
| 85 // Titles are converted to the sync internal format before |
| 86 // being used as keys for the map. |
| 87 typedef base::hash_multimap<std::string, |
| 88 const BookmarkNode*> BookmarkNodeMap; |
| 89 typedef std::pair<BookmarkNodeMap::iterator, |
| 90 BookmarkNodeMap::iterator> BookmarkNodeRange; |
| 91 |
| 92 // Converts and truncates bookmark titles in the form sync does internally |
| 93 // to avoid mismatches due to sync munging titles. |
| 94 void ConvertTitleToSyncInternalFormat( |
| 95 const std::string& input, std::string* output); |
117 | 96 |
118 const BookmarkNode* parent_node_; | 97 const BookmarkNode* parent_node_; |
119 BookmarkNodesSet child_nodes_; | 98 BookmarkNodeMap child_nodes_; |
120 | 99 |
121 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeFinder); | 100 DISALLOW_COPY_AND_ASSIGN(BookmarkNodeFinder); |
122 }; | 101 }; |
123 | 102 |
124 class ScopedAssociationUpdater { | 103 class ScopedAssociationUpdater { |
125 public: | 104 public: |
126 explicit ScopedAssociationUpdater(BookmarkModel* model) { | 105 explicit ScopedAssociationUpdater(BookmarkModel* model) { |
127 model_ = model; | 106 model_ = model; |
128 model->BeginExtensiveChanges(); | 107 model->BeginExtensiveChanges(); |
129 } | 108 } |
130 | 109 |
131 ~ScopedAssociationUpdater() { | 110 ~ScopedAssociationUpdater() { |
132 model_->EndExtensiveChanges(); | 111 model_->EndExtensiveChanges(); |
133 } | 112 } |
134 | 113 |
135 private: | 114 private: |
136 BookmarkModel* model_; | 115 BookmarkModel* model_; |
137 | 116 |
138 DISALLOW_COPY_AND_ASSIGN(ScopedAssociationUpdater); | 117 DISALLOW_COPY_AND_ASSIGN(ScopedAssociationUpdater); |
139 }; | 118 }; |
140 | 119 |
141 BookmarkNodeFinder::BookmarkNodeFinder(const BookmarkNode* parent_node) | 120 BookmarkNodeFinder::BookmarkNodeFinder(const BookmarkNode* parent_node) |
142 : parent_node_(parent_node) { | 121 : parent_node_(parent_node) { |
143 for (int i = 0; i < parent_node_->child_count(); ++i) { | 122 for (int i = 0; i < parent_node_->child_count(); ++i) { |
144 child_nodes_.insert(parent_node_->GetChild(i)); | 123 const BookmarkNode* child_node = parent_node_->GetChild(i); |
| 124 |
| 125 std::string title = base::UTF16ToUTF8(child_node->GetTitle()); |
| 126 ConvertTitleToSyncInternalFormat(title, &title); |
| 127 |
| 128 child_nodes_.insert(std::make_pair(title, child_node)); |
145 } | 129 } |
146 } | 130 } |
147 | 131 |
148 const BookmarkNode* BookmarkNodeFinder::FindBookmarkNode( | 132 const BookmarkNode* BookmarkNodeFinder::FindBookmarkNode( |
149 const GURL& url, const std::string& title, bool is_folder) { | 133 const GURL& url, const std::string& title, bool is_folder) { |
150 // Create a bookmark node from the given bookmark attributes. | 134 // First lookup a range of bookmarks with the same title. |
151 BookmarkNode temp_node(url); | 135 std::string adjusted_title; |
152 temp_node.SetTitle(base::UTF8ToUTF16(title)); | 136 ConvertTitleToSyncInternalFormat(title, &adjusted_title); |
153 if (is_folder) | 137 BookmarkNodeRange range = child_nodes_.equal_range(adjusted_title); |
154 temp_node.set_type(BookmarkNode::FOLDER); | 138 for (BookmarkNodeMap::iterator iter = range.first; |
155 else | 139 iter != range.second; |
156 temp_node.set_type(BookmarkNode::URL); | 140 ++iter) { |
157 | 141 |
158 const BookmarkNode* result = NULL; | 142 // Then within the range match the node by the folder bit |
159 BookmarkNodesSet::iterator iter = child_nodes_.find(&temp_node); | 143 // and the url. |
160 if (iter != child_nodes_.end()) { | 144 const BookmarkNode* node = iter->second; |
161 result = *iter; | 145 if (is_folder == node->is_folder() && url == node->url()) { |
162 // Remove the matched node so we don't match with it again. | 146 // Remove the matched node so we don't match with it again. |
163 child_nodes_.erase(iter); | 147 child_nodes_.erase(iter); |
| 148 return node; |
| 149 } |
164 } | 150 } |
165 | 151 |
166 return result; | 152 return NULL; |
| 153 } |
| 154 |
| 155 void BookmarkNodeFinder::ConvertTitleToSyncInternalFormat( |
| 156 const std::string& input, std::string* output) { |
| 157 syncer::SyncAPINameToServerName(input, output); |
| 158 base::TruncateUTF8ToByteSize(*output, kTitleLimitBytes, output); |
167 } | 159 } |
168 | 160 |
169 // Helper class to build an index of bookmark nodes by their IDs. | 161 // Helper class to build an index of bookmark nodes by their IDs. |
170 class BookmarkNodeIdIndex { | 162 class BookmarkNodeIdIndex { |
171 public: | 163 public: |
172 BookmarkNodeIdIndex() { } | 164 BookmarkNodeIdIndex() { } |
173 ~BookmarkNodeIdIndex() { } | 165 ~BookmarkNodeIdIndex() { } |
174 | 166 |
175 // Adds the given bookmark node and all its descendants to the ID index. | 167 // Adds the given bookmark node and all its descendants to the ID index. |
176 // Does nothing if node is NULL. | 168 // Does nothing if node is NULL. |
(...skipping 616 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
793 syncer::SyncError::PERSISTENCE_ERROR, | 785 syncer::SyncError::PERSISTENCE_ERROR, |
794 message, | 786 message, |
795 syncer::BOOKMARKS); | 787 syncer::BOOKMARKS); |
796 } | 788 } |
797 } | 789 } |
798 } | 790 } |
799 return syncer::SyncError(); | 791 return syncer::SyncError(); |
800 } | 792 } |
801 | 793 |
802 } // namespace browser_sync | 794 } // namespace browser_sync |
OLD | NEW |