Commit cc91f977 authored by csilv@chromium.org's avatar csilv@chromium.org

[ntp4] Observe and process bookmark change notifications from the bookmarks data model.

BUG=93144
Review URL: http://codereview.chromium.org/7713026

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98657 0039d316-1c4b-4281-b951-d872f2087c98
parent c2b2fcf5
...@@ -41,6 +41,9 @@ cr.define('ntp4', function() { ...@@ -41,6 +41,9 @@ cr.define('ntp4', function() {
Bookmark.prototype = { Bookmark.prototype = {
__proto__: HTMLDivElement.prototype, __proto__: HTMLDivElement.prototype,
/**
* Initialize the bookmark object.
*/
initialize: function() { initialize: function() {
var id = tileId++; var id = tileId++;
this.id = 'bookmark_tile_' + id; this.id = 'bookmark_tile_' + id;
...@@ -132,9 +135,12 @@ cr.define('ntp4', function() { ...@@ -132,9 +135,12 @@ cr.define('ntp4', function() {
BookmarkTitle.prototype = { BookmarkTitle.prototype = {
__proto__: HTMLDivElement.prototype, __proto__: HTMLDivElement.prototype,
/**
* Initialize the bookmark title object.
*/
initialize: function(data) { initialize: function(data) {
this.className = 'title-crumb'; this.className = 'title-crumb';
this.folderId_ = data.id; this.folderId = data.id;
this.textContent = data.parentId ? data.title : this.textContent = data.parentId ? data.title :
localStrings.getString('bookmarksPage'); localStrings.getString('bookmarksPage');
...@@ -147,7 +153,7 @@ cr.define('ntp4', function() { ...@@ -147,7 +153,7 @@ cr.define('ntp4', function() {
* @private * @private
*/ */
handleClick_: function(e) { handleClick_: function(e) {
chrome.send('getBookmarksData', [this.folderId_]); chrome.send('getBookmarksData', [this.folderId]);
}, },
}; };
...@@ -182,6 +188,9 @@ cr.define('ntp4', function() { ...@@ -182,6 +188,9 @@ cr.define('ntp4', function() {
BookmarksPage.prototype = { BookmarksPage.prototype = {
__proto__: TilePage.prototype, __proto__: TilePage.prototype,
/**
* Initialize the bookmarks page object.
*/
initialize: function() { initialize: function() {
this.classList.add('bookmarks-page'); this.classList.add('bookmarks-page');
...@@ -243,14 +252,156 @@ cr.define('ntp4', function() { ...@@ -243,14 +252,156 @@ cr.define('ntp4', function() {
} }
}, },
/**
* Determine whether a bookmark ID matches a folder in the current
* hierarchy.
* @param {string} id The bookmark ID to search for.
* @private
*/
isBookmarkInParentHierarchy_: function(id) {
var titlesWrapper = $('bookmarks-title-wrapper');
var titles = titlesWrapper.querySelectorAll('.title-crumb');
for (var i = 0; i < titles.length; i++) {
var bookmarkTitle = titles[i];
if (bookmarkTitle.folderId == id) {
return true;
}
}
return false;
},
/** @inheritDoc */ /** @inheritDoc */
shouldAcceptDrag: function(dataTransfer) { shouldAcceptDrag: function(dataTransfer) {
return false; return false;
}, },
/**
* Invoked before a batch import begins. We will ignore added/changed
* notifications while the operation is in progress.
*/
bookmarkImportBegan: function() {
this.importing = true;
},
/**
* Invoked after a batch import finishes. We will reload the bookmarks
* page to reflect the new state.
*/
bookmarkImportEnded: function() {
this.importing = false;
chrome.send('getBookmarksData', []);
},
/**
* Invoked when a node has been added.
* @param {string} id The id of the newly created bookmark node.
* @param {Object} bookmark The new bookmark node.
*/
bookmarkNodeAdded: function(id, bookmark) {
if (this.importing) return;
if (this.id == bookmark.parentId)
this.addTileAt(new Bookmark(bookmark), bookmark.index, false);
},
/**
* Invoked when the title or url of a node changes.
* @param {string} id The id of the changed node.
* @param {Object} changeInfo Details of the changed node.
*/
bookmarkNodeChanged: function(id, changeInfo) {
if (this.importing) return;
// If the current folder or parent is being re-named, reload the page.
// TODO(csilv): Optimize this to reload just the titles.
if (this.isBookmarkInParentHierarchy_(id)) {
chrome.send('getBookmarksData', [this.id]);
return;
}
// If the target item is contained in this folder, update just that item.
for (var i = 0; i < this.tiles.length; i++) {
var tile = this.tiles[i];
var data = tile.firstChild.data;
if (data.id == id) {
data.title = changeInfo.title;
var title = tile.querySelector('.title');
title.textContent = data.title;
if (changeInfo.url) {
data.url = changeInfo.url;
var button = tile.querySelector('.button');
button.href = title.href = data.url;
}
break;
}
}
},
/**
* Invoked when the children (just direct children, not descendants) of
* a folder have been reordered in some way, such as sorted.
* @param {string} id The id of the reordered bookmark node.
* @param {!Object} reorderInfo Details of the reordered bookmark node.
*/
bookmarkNodeChildrenReordered: function(id, reorderInfo) {
if (this.id == id)
chrome.send('getBookmarksData', [this.id]);
},
/**
* Invoked when a node has moved.
* @param {string} id The id of the moved bookmark node.
* @param {!Object} moveInfo Details of the moved bookmark.
*/
bookmarkNodeMoved: function(id, moveInfo) {
// TODO(csilv): Optimize this by doing less than reloading the folder.
// Reload the current page if the target item is the current folder
// or a parent of the current folder.
if (this.isBookmarkInParentHierarchy_(id)) {
chrome.send('getBookmarksData', [this.id]);
return;
}
// Reload the current page if the target item is being moved to/from the
// current folder.
if (this.id == moveInfo.parentId ||
this.id == moveInfo.oldParentId) {
chrome.send('getBookmarksData', [this.id]);
}
},
/**
* Invoked when a node has been removed from a folder.
* @param {string} id The id of the removed bookmark node.
* @param {!Object} removeInfo Details of the removed bookmark node.
*/
bookmarkNodeRemoved: function(id, removeInfo) {
// If the target item is the visibile folder or a parent folder, load
// the parent of the removed item.
if (this.isBookmarkInParentHierarchy_(id)) {
chrome.send('getBookmarksData', [removeInfo.parentId]);
return;
}
// If the target item is contained in the visible folder, find the
// matching tile and delete it.
if (this.id == removeInfo.parentId) {
for (var i = 0; i < this.tiles.length; i++) {
var tile = this.tiles[i];
if (tile.firstChild.data.id == id) {
this.removeTile(tile, false);
break;
}
}
}
},
/** /**
* Set the bookmark data that should be displayed, replacing any existing * Set the bookmark data that should be displayed, replacing any existing
* data. * data.
* @param {Object} data Data that shoudl be displayed. Contains arrays
* 'items' and 'navigationItems'.
*/ */
set data(data) { set data(data) {
this.id = data.navigationItems[0].id; this.id = data.navigationItems[0].id;
......
...@@ -761,12 +761,47 @@ cr.define('ntp4', function() { ...@@ -761,12 +761,47 @@ cr.define('ntp4', function() {
chrome.send('saveAppPageName', [name, index]); chrome.send('saveAppPageName', [name, index]);
} }
function bookmarkImportBegan() {
bookmarksPage.bookmarkImportBegan();
};
function bookmarkImportEnded() {
bookmarksPage.bookmarkImportEnded();
};
function bookmarkNodeAdded(id, bookmark) {
bookmarksPage.bookmarkNodeAdded(id, bookmark);
};
function bookmarkNodeChanged(id, changeInfo) {
bookmarksPage.bookmarkNodeChanged(id, changeInfo);
};
function bookmarkNodeChildrenReordered(id, reorderInfo) {
bookmarksPage.bookmarkNodeChildrenReordered(id, reorderInfo);
};
function bookmarkNodeMoved(id, moveInfo) {
bookmarksPage.bookmarkNodeMoved(id, moveInfo);
};
function bookmarkNodeRemoved(id, removeInfo) {
bookmarksPage.bookmarkNodeRemoved(id, removeInfo);
};
// Return an object with all the exports // Return an object with all the exports
return { return {
appAdded: appAdded, appAdded: appAdded,
appRemoved: appRemoved, appRemoved: appRemoved,
appsPrefChangeCallback: appsPrefChangeCallback, appsPrefChangeCallback: appsPrefChangeCallback,
assert: assert, assert: assert,
bookmarkImportBegan: bookmarkImportBegan,
bookmarkImportEnded: bookmarkImportEnded,
bookmarkNodeAdded: bookmarkNodeAdded,
bookmarkNodeChanged: bookmarkNodeChanged,
bookmarkNodeChildrenReordered: bookmarkNodeChildrenReordered,
bookmarkNodeMoved: bookmarkNodeMoved,
bookmarkNodeRemoved: bookmarkNodeRemoved,
enterRearrangeMode: enterRearrangeMode, enterRearrangeMode: enterRearrangeMode,
getAppsCallback: getAppsCallback, getAppsCallback: getAppsCallback,
getAppsPageIndex: getAppsPageIndex, getAppsPageIndex: getAppsPageIndex,
......
...@@ -417,6 +417,10 @@ cr.define('ntp4', function() { ...@@ -417,6 +417,10 @@ cr.define('ntp4', function() {
this.onCardChanged.bind(this)); this.onCardChanged.bind(this));
}, },
get tiles() {
return this.tileElements_;
},
get tileCount() { get tileCount() {
return this.tileElements_.length; return this.tileElements_.length;
}, },
...@@ -490,9 +494,11 @@ cr.define('ntp4', function() { ...@@ -490,9 +494,11 @@ cr.define('ntp4', function() {
* Removes the given tile and animates the respositioning of the other * Removes the given tile and animates the respositioning of the other
* tiles. * tiles.
* @param {HTMLElement} tile The tile to remove from |tileGrid_|. * @param {HTMLElement} tile The tile to remove from |tileGrid_|.
* @param {?boolean} animate If true, tiles will animate.
*/ */
removeTile: function(tile) { removeTile: function(tile, animate) {
this.classList.add('animating-tile-page'); if (animate)
this.classList.add('animating-tile-page');
var index = Array.prototype.indexOf.call(this.tileElements_, tile); var index = Array.prototype.indexOf.call(this.tileElements_, tile);
tile.parentNode.removeChild(tile); tile.parentNode.removeChild(tile);
this.calculateLayoutValues_(); this.calculateLayoutValues_();
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/extensions/extension_bookmark_helpers.h" #include "chrome/browser/extensions/extension_bookmark_helpers.h"
#include "chrome/browser/extensions/extension_bookmarks_module_constants.h"
#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_service.h"
...@@ -15,39 +16,135 @@ ...@@ -15,39 +16,135 @@
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "content/common/notification_service.h" #include "content/common/notification_service.h"
// TODO(csilv):
// Much of this implementation is based on the classes defined in
// extension_bookmarks_module.cc. Longer term we should consider migrating
// NTP into an embedded extension which would allow us to leverage the same
// bookmark APIs as the bookmark manager.
namespace keys = extension_bookmarks_module_constants;
BookmarksHandler::BookmarksHandler() { BookmarksHandler::BookmarksHandler() {
// TODO(csilv): Register for bookmark model change notifications.
} }
BookmarksHandler::~BookmarksHandler() {} BookmarksHandler::~BookmarksHandler() {
if (model_)
model_->RemoveObserver(this);
}
WebUIMessageHandler* BookmarksHandler::Attach(WebUI* web_ui) {
WebUIMessageHandler::Attach(web_ui);
model_ = Profile::FromWebUI(web_ui)->GetBookmarkModel();
if (model_)
model_->AddObserver(this);
return this;
}
void BookmarksHandler::RegisterMessages() { void BookmarksHandler::RegisterMessages() {
web_ui_->RegisterMessageCallback("getBookmarksData", web_ui_->RegisterMessageCallback("getBookmarksData",
NewCallback(this, &BookmarksHandler::HandleGetBookmarksData)); NewCallback(this, &BookmarksHandler::HandleGetBookmarksData));
} }
void BookmarksHandler::Observe(int type, void BookmarksHandler::Loaded(BookmarkModel* model, bool ids_reassigned) {
const NotificationSource& source, if (getBookmarksDataIsPending_) {
const NotificationDetails& details) { HandleGetBookmarksData(NULL);
// TODO(csilv): Update UI based on changes to bookmark notifications. getBookmarksDataIsPending_ = false;
switch (type) {
case chrome::NOTIFICATION_BOOKMARK_MODEL_LOADED: {
registrar_.Remove(this, chrome::NOTIFICATION_BOOKMARK_MODEL_LOADED,
Source<Profile>(Profile::FromWebUI(web_ui_)));
HandleGetBookmarksData(NULL);
break;
}
} }
} }
void BookmarksHandler::BookmarkModelBeingDeleted(BookmarkModel* model) {
// If this occurs it probably means that this tab will close shortly.
// Discard our reference to the model so that we won't use it again.
model_ = NULL;
}
void BookmarksHandler::BookmarkNodeMoved(BookmarkModel* model,
const BookmarkNode* old_parent, int old_index,
const BookmarkNode* new_parent, int new_index) {
const BookmarkNode* node = new_parent->GetChild(new_index);
StringValue id(base::Int64ToString(node->id()));
DictionaryValue move_info;
move_info.SetString(keys::kParentIdKey,
base::Int64ToString(new_parent->id()));
move_info.SetInteger(keys::kIndexKey, new_index);
move_info.SetString(keys::kOldParentIdKey,
base::Int64ToString(old_parent->id()));
move_info.SetInteger(keys::kOldIndexKey, old_index);
web_ui_->CallJavascriptFunction("ntp4.bookmarkNodeMoved", id, move_info);
}
void BookmarksHandler::BookmarkNodeAdded(BookmarkModel* model,
const BookmarkNode* parent, int index) {
const BookmarkNode* node = parent->GetChild(index);
StringValue id(base::Int64ToString(node->id()));
scoped_ptr<DictionaryValue> node_info(
extension_bookmark_helpers::GetNodeDictionary(node, false, false));
web_ui_->CallJavascriptFunction("ntp4.bookmarkNodeAdded", id, *node_info);
}
void BookmarksHandler::BookmarkNodeRemoved(BookmarkModel* model,
const BookmarkNode* parent, int index, const BookmarkNode* node) {
StringValue id(base::Int64ToString(node->id()));
DictionaryValue remove_info;
remove_info.SetString(keys::kParentIdKey,
base::Int64ToString(parent->id()));
remove_info.SetInteger(keys::kIndexKey, index);
web_ui_->CallJavascriptFunction("ntp4.bookmarkNodeRemoved", id, remove_info);
}
void BookmarksHandler::BookmarkNodeChanged(BookmarkModel* model,
const BookmarkNode* node) {
StringValue id(base::Int64ToString(node->id()));
DictionaryValue change_info;
change_info.SetString(keys::kTitleKey, node->GetTitle());
if (node->is_url())
change_info.SetString(keys::kUrlKey, node->url().spec());
web_ui_->CallJavascriptFunction("ntp4.bookmarkNodeChanged", id, change_info);
}
void BookmarksHandler::BookmarkNodeFaviconChanged(BookmarkModel* model,
const BookmarkNode* node) {
// Favicons are handled by through use of the chrome://favicon protocol, so
// there's nothing for us to do here (but we need to provide an
// implementation).
}
void BookmarksHandler::BookmarkNodeChildrenReordered(BookmarkModel* model,
const BookmarkNode* node) {
StringValue id(base::Int64ToString(node->id()));
int childCount = node->child_count();
ListValue* children = new ListValue();
for (int i = 0; i < childCount; ++i) {
const BookmarkNode* child = node->GetChild(i);
Value* child_id = new StringValue(base::Int64ToString(child->id()));
children->Append(child_id);
}
DictionaryValue reorder_info;
reorder_info.Set(keys::kChildIdsKey, children);
web_ui_->CallJavascriptFunction("ntp4.bookmarkNodeChildrenReordered", id,
reorder_info);
}
void BookmarksHandler::BookmarkImportBeginning(BookmarkModel* model) {
web_ui_->CallJavascriptFunction("ntp4.bookmarkImportBegan");
}
void BookmarksHandler::BookmarkImportEnding(BookmarkModel* model) {
web_ui_->CallJavascriptFunction("ntp4.bookmarkImportEnded");
}
void BookmarksHandler::HandleGetBookmarksData(const base::ListValue* args) { void BookmarksHandler::HandleGetBookmarksData(const base::ListValue* args) {
// At startup, Bookmarks may not be fully loaded. If this is the case, // At startup, Bookmarks may not be fully loaded. If this is the case,
// we'll wait for the notification to arrive. // we'll wait for the notification to arrive.
Profile* profile = Profile::FromWebUI(web_ui_); Profile* profile = Profile::FromWebUI(web_ui_);
BookmarkModel* model = profile->GetBookmarkModel(); BookmarkModel* model = profile->GetBookmarkModel();
if (!model->IsLoaded()) { if (!model->IsLoaded()) {
registrar_.Add(this, chrome::NOTIFICATION_BOOKMARK_MODEL_LOADED, getBookmarksDataIsPending_ = true;
Source<Profile>(profile));
return; return;
} }
...@@ -65,7 +162,7 @@ void BookmarksHandler::HandleGetBookmarksData(const base::ListValue* args) { ...@@ -65,7 +162,7 @@ void BookmarksHandler::HandleGetBookmarksData(const base::ListValue* args) {
const BookmarkNode* node = model->GetNodeByID(id); const BookmarkNode* node = model->GetNodeByID(id);
if (!node) if (!node)
return; node = model->root_node();
// We wish to merge the root node with the bookmarks bar node. // We wish to merge the root node with the bookmarks bar node.
if (model->is_root_node(node)) if (model->is_root_node(node))
...@@ -96,7 +193,6 @@ void BookmarksHandler::HandleGetBookmarksData(const base::ListValue* args) { ...@@ -96,7 +193,6 @@ void BookmarksHandler::HandleGetBookmarksData(const base::ListValue* args) {
// static // static
void BookmarksHandler::RegisterUserPrefs(PrefService* prefs) { void BookmarksHandler::RegisterUserPrefs(PrefService* prefs) {
// Default folder is the root node. // Default folder is the root node.
// TODO(csilv): Should we default to the Bookmarks bar?
// TODO(csilv): Should we sync this preference? // TODO(csilv): Should we sync this preference?
prefs->RegisterInt64Pref(prefs::kNTPShownBookmarksFolder, 0, prefs->RegisterInt64Pref(prefs::kNTPShownBookmarksFolder, 0,
PrefService::UNSYNCABLE_PREF); PrefService::UNSYNCABLE_PREF);
......
...@@ -6,27 +6,46 @@ ...@@ -6,27 +6,46 @@
#define CHROME_BROWSER_UI_WEBUI_NTP_BOOKMARKS_HANDLER_H_ #define CHROME_BROWSER_UI_WEBUI_NTP_BOOKMARKS_HANDLER_H_
#pragma once #pragma once
#include "chrome/browser/bookmarks/bookmark_model_observer.h"
#include "content/browser/webui/web_ui.h" #include "content/browser/webui/web_ui.h"
#include "content/common/notification_observer.h"
#include "content/common/notification_registrar.h"
class PrefService; class PrefService;
class Profile; class Profile;
// The handler for Javascript messages related to the "bookmarks" view. // The handler for Javascript messages related to the "bookmarks" view.
class BookmarksHandler : public WebUIMessageHandler, class BookmarksHandler : public WebUIMessageHandler,
public NotificationObserver { public BookmarkModelObserver {
public: public:
explicit BookmarksHandler(); explicit BookmarksHandler();
virtual ~BookmarksHandler(); virtual ~BookmarksHandler();
// WebUIMessageHandler implementation. // WebUIMessageHandler implementation.
virtual WebUIMessageHandler* Attach(WebUI* web_ui) OVERRIDE;
virtual void RegisterMessages() OVERRIDE; virtual void RegisterMessages() OVERRIDE;
// NotificationObserver // BookmarkModelObserver implementation.
virtual void Observe(int type, virtual void Loaded(BookmarkModel* model, bool ids_reassigned) OVERRIDE;
const NotificationSource& source, virtual void BookmarkModelBeingDeleted(BookmarkModel* model) OVERRIDE;
const NotificationDetails& details) OVERRIDE; virtual void BookmarkNodeMoved(BookmarkModel* model,
const BookmarkNode* old_parent,
int old_index,
const BookmarkNode* new_parent,
int new_index) OVERRIDE;
virtual void BookmarkNodeAdded(BookmarkModel* model,
const BookmarkNode* parent,
int index) OVERRIDE;
virtual void BookmarkNodeRemoved(BookmarkModel* model,
const BookmarkNode* parent,
int old_index,
const BookmarkNode* node) OVERRIDE;
virtual void BookmarkNodeChanged(BookmarkModel* model,
const BookmarkNode* node) OVERRIDE;
virtual void BookmarkNodeFaviconChanged(BookmarkModel* model,
const BookmarkNode* node) OVERRIDE;
virtual void BookmarkNodeChildrenReordered(BookmarkModel* model,
const BookmarkNode* node) OVERRIDE;
virtual void BookmarkImportBeginning(BookmarkModel* model) OVERRIDE;
virtual void BookmarkImportEnding(BookmarkModel* model) OVERRIDE;
// Callback for the "getBookmarksData" message. // Callback for the "getBookmarksData" message.
void HandleGetBookmarksData(const base::ListValue* args); void HandleGetBookmarksData(const base::ListValue* args);
...@@ -35,7 +54,8 @@ class BookmarksHandler : public WebUIMessageHandler, ...@@ -35,7 +54,8 @@ class BookmarksHandler : public WebUIMessageHandler,
static void RegisterUserPrefs(PrefService* prefs); static void RegisterUserPrefs(PrefService* prefs);
private: private:
NotificationRegistrar registrar_; BookmarkModel* model_; // weak
bool getBookmarksDataIsPending_;
DISALLOW_COPY_AND_ASSIGN(BookmarksHandler); DISALLOW_COPY_AND_ASSIGN(BookmarksHandler);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment