1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/starred_url_database.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "app/sql/statement.h"
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h"
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/json/json_writer.h"
10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/logging.h"
11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_vector.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/stl_util-inl.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/values.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/bookmarks/bookmark_codec.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/bookmarks/bookmark_model.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/history.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/query_parser.h"
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The following table is used to store star (aka bookmark) information. This
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// class derives from URLDatabase, which has its own schema.
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// starred
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   id                 Unique identifier (primary key) for the entry.
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   type               Type of entry, if 0 this corresponds to a URL, 1 for
27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen//                      a system folder, 2 for a user created folder, 3 for
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//                      other.
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   url_id             ID of the url, only valid if type == 0
30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen//   group_id           ID of the folder, only valid if type != 0. This id comes
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//                      from the UI and is NOT the same as id.
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   title              User assigned title.
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   date_added         Creation date.
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   visual_order       Visual order within parent.
35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen//   parent_id          Folder ID of the parent this entry is contained in, if 0
36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen//                      entry is not in a folder.
37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen//   date_modified      Time the folder was last modified. See comments in
38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen//                      StarredEntry::date_folder_modified
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// NOTE: group_id and parent_id come from the UI, id is assigned by the
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// db.
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace history {
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Fields used by FillInStarredEntry.
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#define STAR_FIELDS \
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    " starred.id, starred.type, starred.title, starred.date_added, " \
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    "starred.visual_order, starred.parent_id, urls.url, urls.id, " \
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    "starred.group_id, starred.date_modified "
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst char kHistoryStarFields[] = STAR_FIELDS;
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid FillInStarredEntry(const sql::Statement& s, StarredEntry* entry) {
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(entry);
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->id = s.ColumnInt64(0);
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (s.ColumnInt(1)) {
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case 0:
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      entry->type = history::StarredEntry::URL;
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      entry->url = GURL(s.ColumnString(6));
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case 1:
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      entry->type = history::StarredEntry::BOOKMARK_BAR;
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case 2:
65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      entry->type = history::StarredEntry::USER_FOLDER;
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case 3:
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      entry->type = history::StarredEntry::OTHER;
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->title = s.ColumnString16(2);
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->date_added = base::Time::FromInternalValue(s.ColumnInt64(3));
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->visual_order = s.ColumnInt(4);
77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  entry->parent_folder_id = s.ColumnInt64(5);
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->url_id = s.ColumnInt64(7);
79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  entry->folder_id = s.ColumnInt64(8);
80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  entry->date_folder_modified = base::Time::FromInternalValue(s.ColumnInt64(9));
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochStarredURLDatabase::StarredURLDatabase() {
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochStarredURLDatabase::~StarredURLDatabase() {
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::MigrateBookmarksToFile(const FilePath& path) {
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!GetDB().DoesTableExist("starred"))
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (EnsureStarredIntegrity() && !MigrateBookmarksToFileImpl(path)) {
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << " Bookmarks migration failed";
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!GetDB().Execute("DROP TABLE starred")) {
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << "Unable to drop starred table";
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::GetAllStarredEntries(
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::vector<StarredEntry>* entries) {
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(entries);
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string sql = "SELECT ";
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql.append(kHistoryStarFields);
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql.append("FROM starred LEFT JOIN urls ON starred.url_id = urls.id ");
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql += "ORDER BY parent_id, visual_order";
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement s(GetDB().GetUniqueStatement(sql.c_str()));
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!s) {
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << "Statement prepare failed";
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  history::StarredEntry entry;
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  while (s.Step()) {
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FillInStarredEntry(s, &entry);
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Reset the url for non-url types. This is needed as we're reusing the
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // same entry for the loop.
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (entry.type != history::StarredEntry::URL)
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      entry.url = GURL();
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entries->push_back(entry);
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::EnsureStarredIntegrity() {
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::set<StarredNode*> roots;
135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::set<StarID> folders_with_duplicate_ids;
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::set<StarredNode*> unparented_urls;
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::set<StarID> empty_url_ids;
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!BuildStarNodes(&roots, &folders_with_duplicate_ids, &unparented_urls,
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                      &empty_url_ids)) {
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool valid = EnsureStarredIntegrityImpl(&roots, folders_with_duplicate_ids,
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          &unparented_urls, empty_url_ids);
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  STLDeleteElements(&roots);
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  STLDeleteElements(&unparented_urls);
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return valid;
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::UpdateStarredEntryRow(StarID star_id,
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                               const string16& title,
154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                               UIStarID parent_folder_id,
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                               int visual_order,
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                               base::Time date_modified) {
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(star_id && visual_order >= 0);
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "UPDATE starred SET title=?, parent_id=?, visual_order=?, "
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "date_modified=? WHERE id=?"));
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindString16(0, title);
165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  statement.BindInt64(1, parent_folder_id);
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(2, visual_order);
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(3, date_modified.ToInternalValue());
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(4, star_id);
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return statement.Run();
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool StarredURLDatabase::AdjustStarredVisualOrder(UIStarID parent_folder_id,
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                  int start_visual_order,
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                  int delta) {
175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(parent_folder_id && start_visual_order >= 0);
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "UPDATE starred SET visual_order=visual_order+? "
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "WHERE parent_id=? AND visual_order >= ?"));
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(0, delta);
183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  statement.BindInt64(1, parent_folder_id);
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(2, start_visual_order);
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return statement.Run();
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochStarID StarredURLDatabase::CreateStarredEntryRow(URLID url_id,
189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                                 UIStarID folder_id,
190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                                 UIStarID parent_folder_id,
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                 const string16& title,
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                 const base::Time& date_added,
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                 int visual_order,
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                 StarredEntry::Type type) {
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(visual_order >= 0 &&
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch         (type != history::StarredEntry::URL || url_id));
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "INSERT INTO starred "
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "(type, url_id, group_id, title, date_added, visual_order, parent_id, "
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "date_modified) VALUES (?,?,?,?,?,?,?,?)"));
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (type) {
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case history::StarredEntry::URL:
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      statement.BindInt(0, 0);
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case history::StarredEntry::BOOKMARK_BAR:
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      statement.BindInt(0, 1);
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    case history::StarredEntry::USER_FOLDER:
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      statement.BindInt(0, 2);
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case history::StarredEntry::OTHER:
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      statement.BindInt(0, 3);
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(1, url_id);
221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  statement.BindInt64(2, folder_id);
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindString16(3, title);
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(4, date_added.ToInternalValue());
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(5, visual_order);
225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  statement.BindInt64(6, parent_folder_id);
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(7, base::Time().ToInternalValue());
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (statement.Run())
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return GetDB().GetLastInsertRowId();
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return 0;
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::DeleteStarredEntryRow(StarID star_id) {
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "DELETE FROM starred WHERE id=?"));
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, star_id);
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return statement.Run();
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::GetStarredEntry(StarID star_id, StarredEntry* entry) {
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(entry && star_id);
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT" STAR_FIELDS "FROM starred LEFT JOIN urls ON "
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "starred.url_id = urls.id WHERE starred.id=?"));
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, star_id);
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (statement.Step()) {
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FillInStarredEntry(statement, entry);
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochStarID StarredURLDatabase::CreateStarredEntry(StarredEntry* entry) {
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry->id = 0;  // Ensure 0 for failure case.
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Adjust the visual order when we are inserting it somewhere.
263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (entry->parent_folder_id)
264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    AdjustStarredVisualOrder(entry->parent_folder_id, entry->visual_order, 1);
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Insert the new entry.
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (entry->type) {
268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    case StarredEntry::USER_FOLDER:
269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      entry->id = CreateStarredEntryRow(0, entry->folder_id,
270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          entry->parent_folder_id, entry->title, entry->date_added,
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          entry->visual_order, entry->type);
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case StarredEntry::URL: {
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Get the row for this URL.
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      URLRow url_row;
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!GetRowForURL(entry->url, &url_row)) {
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // Create a new URL row for this entry.
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        url_row = URLRow(entry->url);
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        url_row.set_title(entry->title);
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        url_row.set_hidden(false);
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        entry->url_id = this->AddURL(url_row);
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        entry->url_id = url_row.id();  // The caller doesn't have to set this.
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Create the star entry referring to the URL row.
288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      entry->id = CreateStarredEntryRow(entry->url_id, entry->folder_id,
289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          entry->parent_folder_id, entry->title, entry->date_added,
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          entry->visual_order, entry->type);
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Update the URL row to refer to this new starred entry.
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      UpdateURLRow(entry->url_id, url_row);
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return entry->id;
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenUIStarID StarredURLDatabase::GetMaxFolderID() {
305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  sql::Statement max_folder_id_statement(GetDB().GetUniqueStatement(
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT MAX(group_id) FROM starred"));
307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!max_folder_id_statement) {
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << GetDB().GetErrorMessage();
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
311ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!max_folder_id_statement.Step()) {
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << GetDB().GetErrorMessage();
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
315ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return max_folder_id_statement.ColumnInt64(0);
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::BuildStarNodes(
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::set<StarredURLDatabase::StarredNode*>* roots,
320ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    std::set<StarID>* folders_with_duplicate_ids,
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::set<StarredNode*>* unparented_urls,
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::set<StarID>* empty_url_ids) {
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<StarredEntry> star_entries;
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!GetAllStarredEntries(&star_entries)) {
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << "Unable to get bookmarks from database";
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Create the folder/bookmark-bar/other nodes.
330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::map<UIStarID, StarredNode*> folder_id_to_node_map;
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < star_entries.size(); ++i) {
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (star_entries[i].type != StarredEntry::URL) {
333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (folder_id_to_node_map.find(star_entries[i].folder_id) !=
334ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          folder_id_to_node_map.end()) {
335ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        // There's already a folder with this ID.
336ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        folders_with_duplicate_ids->insert(star_entries[i].id);
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // Create the node and update the mapping.
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        StarredNode* node = new StarredNode(star_entries[i]);
340ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        folder_id_to_node_map[star_entries[i].folder_id] = node;
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Iterate again, creating nodes for URL bookmarks and parenting all
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // bookmarks/folders. In addition populate the empty_url_ids with all entries
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // of type URL that have an empty URL.
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::map<StarID, StarredNode*> id_to_node_map;
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < star_entries.size(); ++i) {
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (star_entries[i].type == StarredEntry::URL) {
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (star_entries[i].url.is_empty()) {
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        empty_url_ids->insert(star_entries[i].id);
353ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      } else if (!star_entries[i].parent_folder_id ||
354ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          folder_id_to_node_map.find(star_entries[i].parent_folder_id) ==
355ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          folder_id_to_node_map.end()) {
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // This entry has no parent, or we couldn't find the parent.
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        StarredNode* node = new StarredNode(star_entries[i]);
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        unparented_urls->insert(node);
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // Add the node to its parent.
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        StarredNode* parent =
362ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            folder_id_to_node_map[star_entries[i].parent_folder_id];
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        StarredNode* node = new StarredNode(star_entries[i]);
364ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        parent->Add(node, parent->child_count());
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
366ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else if (folders_with_duplicate_ids->find(star_entries[i].id) ==
367ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen               folders_with_duplicate_ids->end()) {
368ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // The entry is a folder (or bookmark bar/other node) that isn't
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // marked as a duplicate.
370ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (!star_entries[i].parent_folder_id ||
371ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          folder_id_to_node_map.find(star_entries[i].parent_folder_id) ==
372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          folder_id_to_node_map.end()) {
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // Entry has no parent, or the parent wasn't found.
374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        roots->insert(folder_id_to_node_map[star_entries[i].folder_id]);
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
376ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        // Parent the folder node.
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        StarredNode* parent =
378ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            folder_id_to_node_map[star_entries[i].parent_folder_id];
379ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        StarredNode* node = folder_id_to_node_map[star_entries[i].folder_id];
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (!node->HasAncestor(parent) && !parent->HasAncestor(node)) {
381ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          parent->Add(node, parent->child_count());
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        } else {
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          // The node has a cycle. Add it to the list of roots so the cycle is
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          // broken.
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          roots->insert(node);
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochStarredURLDatabase::StarredNode* StarredURLDatabase::GetNodeByType(
394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::set<StarredURLDatabase::StarredNode*>& nodes,
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StarredEntry::Type type) {
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::set<StarredNode*>::const_iterator i = nodes.begin();
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       i != nodes.end(); ++i) {
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if ((*i)->value.type == type)
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return *i;
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return NULL;
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::EnsureVisualOrder(
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StarredURLDatabase::StarredNode* node) {
406ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (int i = 0; i < node->child_count(); ++i) {
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (node->GetChild(i)->value.visual_order != i) {
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      StarredEntry& entry = node->GetChild(i)->value;
409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      entry.visual_order = i;
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      LOG(WARNING) << "Bookmark visual order is wrong";
411ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (!UpdateStarredEntryRow(entry.id, entry.title, entry.parent_folder_id,
412ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                 i, entry.date_folder_modified)) {
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        NOTREACHED() << "Unable to update visual order";
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return false;
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!EnsureVisualOrder(node->GetChild(i)))
418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::EnsureStarredIntegrityImpl(
424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::set<StarredURLDatabase::StarredNode*>* roots,
425ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const std::set<StarID>& folders_with_duplicate_ids,
426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::set<StarredNode*>* unparented_urls,
427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::set<StarID>& empty_url_ids) {
428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure the bookmark bar entry exists.
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StarredNode* bookmark_node =
430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      GetNodeByType(*roots, StarredEntry::BOOKMARK_BAR);
431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!bookmark_node) {
432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(WARNING) << "No bookmark bar folder in database";
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If there is no bookmark bar entry in the db things are really
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // screwed. Return false, which won't trigger migration and we'll just
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // drop the tables.
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure the other node exists.
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StarredNode* other_node = GetNodeByType(*roots, StarredEntry::OTHER);
441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!other_node) {
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(WARNING) << "No bookmark other folder in database";
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StarredEntry entry;
444ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    entry.folder_id = GetMaxFolderID() + 1;
445ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (entry.folder_id == 1) {
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED() << "Unable to get new id for other bookmarks folder";
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.id = CreateStarredEntryRow(
450ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        0, entry.folder_id, 0, UTF8ToUTF16("other"), base::Time::Now(), 0,
451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        history::StarredEntry::OTHER);
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!entry.id) {
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED() << "Unable to create other bookmarks folder";
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    entry.type = StarredEntry::OTHER;
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StarredNode* other_node = new StarredNode(entry);
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    roots->insert(other_node);
459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
461ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // We could potentially make sure only one folder with type
462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // BOOKMARK_BAR/OTHER, but history backend enforces this.
463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Nuke any entries with no url.
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::set<StarID>::const_iterator i = empty_url_ids.begin();
466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       i != empty_url_ids.end(); ++i) {
467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(WARNING) << "Bookmark exists with no URL";
468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!DeleteStarredEntryRow(*i)) {
469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED() << "Unable to delete bookmark";
470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure the visual order of the nodes is correct.
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::set<StarredNode*>::const_iterator i = roots->begin();
476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       i != roots->end(); ++i) {
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!EnsureVisualOrder(*i))
478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Move any unparented bookmarks to the bookmark bar.
482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  {
483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::set<StarredNode*>::iterator i = unparented_urls->begin();
484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    while (i != unparented_urls->end()) {
485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      LOG(WARNING) << "Bookmark not in a bookmark folder found";
486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!Move(*i, bookmark_node))
487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return false;
488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      unparented_urls->erase(i++);
489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
492ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Nuke any folders with duplicate ids. A duplicate id means there are two
493ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // folders in the starred table with the same folder_id. We only keep the
494ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // first folder, all other folders are removed.
495ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (std::set<StarID>::const_iterator i = folders_with_duplicate_ids.begin();
496ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen       i != folders_with_duplicate_ids.end(); ++i) {
497ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    LOG(WARNING) << "Duplicate folder id in bookmark database";
498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!DeleteStarredEntryRow(*i)) {
499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED() << "Unable to delete folder";
500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
504ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Move unparented user folders back to the bookmark bar.
505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  {
506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::set<StarredNode*>::iterator i = roots->begin();
507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    while (i != roots->end()) {
508ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if ((*i)->value.type == StarredEntry::USER_FOLDER) {
509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        LOG(WARNING) << "Bookmark folder not on bookmark bar found";
510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (!Move(*i, bookmark_node))
511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          return false;
512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        roots->erase(i++);
513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        ++i;
515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::Move(StarredNode* source, StarredNode* new_parent) {
523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  history::StarredEntry& entry = source->value;
524ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  entry.visual_order = new_parent->child_count();
525ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  entry.parent_folder_id = new_parent->value.folder_id;
526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!UpdateStarredEntryRow(entry.id, entry.title,
527ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                             entry.parent_folder_id, entry.visual_order,
528ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                             entry.date_folder_modified)) {
529c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << "Unable to move folder";
530c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
531c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
532ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  new_parent->Add(source, new_parent->child_count());
533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool StarredURLDatabase::MigrateBookmarksToFileImpl(const FilePath& path) {
537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<history::StarredEntry> entries;
538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!GetAllStarredEntries(&entries))
539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create the bookmark bar and other folder nodes.
542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  history::StarredEntry entry;
543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry.type = history::StarredEntry::BOOKMARK_BAR;
544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BookmarkNode bookmark_bar_node(0, GURL());
545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bookmark_bar_node.Reset(entry);
546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  entry.type = history::StarredEntry::OTHER;
547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BookmarkNode other_node(0, GURL());
548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  other_node.Reset(entry);
549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
550ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::map<history::UIStarID, history::StarID> folder_id_to_id_map;
551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  typedef std::map<history::StarID, BookmarkNode*> IDToNodeMap;
552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  IDToNodeMap id_to_node_map;
553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
554ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  history::UIStarID other_folder_folder_id = 0;
555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  history::StarID other_folder_id = 0;
556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
557ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Iterate through the entries building a mapping between folder_id and id.
558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::vector<history::StarredEntry>::const_iterator i = entries.begin();
559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       i != entries.end(); ++i) {
560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (i->type != history::StarredEntry::URL) {
561ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      folder_id_to_id_map[i->folder_id] = i->id;
562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (i->type == history::StarredEntry::OTHER) {
563c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        other_folder_id = i->id;
564ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        other_folder_folder_id = i->folder_id;
565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Register the bookmark bar and other folder nodes in the maps.
570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  id_to_node_map[HistoryService::kBookmarkBarID] = &bookmark_bar_node;
571ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  folder_id_to_id_map[HistoryService::kBookmarkBarID] =
572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      HistoryService::kBookmarkBarID;
573ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (other_folder_folder_id) {
574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    id_to_node_map[other_folder_id] = &other_node;
575ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    folder_id_to_id_map[other_folder_folder_id] = other_folder_id;
576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Iterate through the entries again creating the nodes.
579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::vector<history::StarredEntry>::iterator i = entries.begin();
580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       i != entries.end(); ++i) {
581ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!i->parent_folder_id) {
582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      DCHECK(i->type == history::StarredEntry::BOOKMARK_BAR ||
583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch             i->type == history::StarredEntry::OTHER);
584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Only entries with no parent should be the bookmark bar and other
585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // bookmarks folders.
586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BookmarkNode* node = id_to_node_map[i->id];
590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!node) {
591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Creating a node results in creating the parent. As such, it is
592ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // possible for the node representing a folder to have been created before
593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // encountering the details.
594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // The created nodes are owned by the root node.
596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      node = new BookmarkNode(0, i->url);
597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      id_to_node_map[i->id] = node;
598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    node->Reset(*i);
600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
601ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DCHECK(folder_id_to_id_map.find(i->parent_folder_id) !=
602ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen           folder_id_to_id_map.end());
603ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    history::StarID parent_id = folder_id_to_id_map[i->parent_folder_id];
604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BookmarkNode* parent = id_to_node_map[parent_id];
605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!parent) {
606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Haven't encountered the parent yet, create it now.
607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      parent = new BookmarkNode(0, GURL());
608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      id_to_node_map[parent_id] = parent;
609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Add the node to its parent. |entries| is ordered by parent then
612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // visual order so that we know we maintain visual order by always adding
613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // to the end.
614ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    parent->Add(node, parent->child_count());
615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Save to file.
618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BookmarkCodec encoder;
619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<Value> encoded_bookmarks(
620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      encoder.Encode(&bookmark_bar_node, &other_node));
621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string content;
622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  base::JSONWriter::Write(encoded_bookmarks.get(), true, &content);
623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return (file_util::WriteFile(path, content.c_str(),
625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               static_cast<int>(content.length())) != -1);
626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace history
629