summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/automation/automation_provider.cc200
-rw-r--r--chrome/browser/automation/automation_provider.h32
-rw-r--r--chrome/browser/automation/automation_provider_observers.cc22
-rw-r--r--chrome/browser/automation/automation_provider_observers.h47
-rw-r--r--chrome/test/automation/automation_messages_internal.h47
-rw-r--r--chrome/test/automation/browser_proxy.cc89
-rw-r--r--chrome/test/automation/browser_proxy.h19
-rw-r--r--chrome/test/functional/bookmarks.py118
-rw-r--r--chrome/test/functional/pyauto_test_utils.py22
-rw-r--r--chrome/test/pyautolib/bookmark_model.py105
-rw-r--r--chrome/test/pyautolib/pyauto.py10
-rw-r--r--chrome/test/pyautolib/pyautolib.cc86
-rw-r--r--chrome/test/pyautolib/pyautolib.h22
-rw-r--r--chrome/test/pyautolib/pyautolib.i24
-rw-r--r--ipc/ipc_message_macros.h76
15 files changed, 919 insertions, 0 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc
index 47db0d6..e083b60 100644
--- a/chrome/browser/automation/automation_provider.cc
+++ b/chrome/browser/automation/automation_provider.cc
@@ -18,6 +18,7 @@
#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "base/thread.h"
+#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/app_modal_dialog.h"
@@ -28,6 +29,8 @@
#include "chrome/browser/automation/extension_automation_constants.h"
#include "chrome/browser/automation/extension_port_container.h"
#include "chrome/browser/blocked_popup_container.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/bookmarks/bookmark_storage.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/chrome_thread.h"
@@ -409,6 +412,22 @@ void AutomationProvider::OnMessageReceived(const IPC::Message& message) {
HandleFindWindowLocationRequest)
IPC_MESSAGE_HANDLER(AutomationMsg_BookmarkBarVisibility,
GetBookmarkBarVisibility)
+ IPC_MESSAGE_HANDLER(AutomationMsg_GetBookmarksAsJSON,
+ GetBookmarksAsJSON)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_WaitForBookmarkModelToLoad,
+ WaitForBookmarkModelToLoad)
+ IPC_MESSAGE_HANDLER(AutomationMsg_AddBookmarkGroup,
+ AddBookmarkGroup)
+ IPC_MESSAGE_HANDLER(AutomationMsg_AddBookmarkURL,
+ AddBookmarkURL)
+ IPC_MESSAGE_HANDLER(AutomationMsg_ReparentBookmark,
+ ReparentBookmark)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetBookmarkTitle,
+ SetBookmarkTitle)
+ IPC_MESSAGE_HANDLER(AutomationMsg_SetBookmarkURL,
+ SetBookmarkURL)
+ IPC_MESSAGE_HANDLER(AutomationMsg_RemoveBookmark,
+ RemoveBookmark)
IPC_MESSAGE_HANDLER(AutomationMsg_GetInfoBarCount, GetInfoBarCount)
IPC_MESSAGE_HANDLER_DELAY_REPLY(AutomationMsg_ClickInfoBarAccept,
ClickInfoBarAccept)
@@ -1285,6 +1304,187 @@ void AutomationProvider::GetBookmarkBarVisibility(int handle,
}
}
+void AutomationProvider::GetBookmarksAsJSON(int handle,
+ std::string* bookmarks_as_json,
+ bool *success) {
+ *success = false;
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ if (!browser->profile()->GetBookmarkModel()->IsLoaded()) {
+ return;
+ }
+ scoped_refptr<BookmarkStorage> storage = new BookmarkStorage(
+ browser->profile(),
+ browser->profile()->GetBookmarkModel());
+ *success = storage->SerializeData(bookmarks_as_json);
+ }
+ }
+}
+
+void AutomationProvider::WaitForBookmarkModelToLoad(
+ int handle,
+ IPC::Message* reply_message) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (model->IsLoaded()) {
+ AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
+ reply_message, true);
+ Send(reply_message);
+ } else {
+ // The observer will delete itself when done.
+ new AutomationProviderBookmarkModelObserver(this, reply_message,
+ model);
+ }
+ }
+}
+
+void AutomationProvider::AddBookmarkGroup(int handle,
+ int64 parent_id, int index,
+ std::wstring title,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* parent = model->GetNodeByID(parent_id);
+ DCHECK(parent);
+ if (parent) {
+ const BookmarkNode* child = model->AddGroup(parent, index,
+ WideToUTF16(title));
+ DCHECK(child);
+ if (child)
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+void AutomationProvider::AddBookmarkURL(int handle,
+ int64 parent_id, int index,
+ std::wstring title, const GURL& url,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* parent = model->GetNodeByID(parent_id);
+ DCHECK(parent);
+ if (parent) {
+ const BookmarkNode* child = model->AddURL(parent, index,
+ WideToUTF16(title), url);
+ DCHECK(child);
+ if (child)
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+void AutomationProvider::ReparentBookmark(int handle,
+ int64 id, int64 new_parent_id,
+ int index,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* node = model->GetNodeByID(id);
+ DCHECK(node);
+ const BookmarkNode* new_parent = model->GetNodeByID(new_parent_id);
+ DCHECK(new_parent);
+ if (node && new_parent) {
+ model->Move(node, new_parent, index);
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+void AutomationProvider::SetBookmarkTitle(int handle,
+ int64 id, std::wstring title,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* node = model->GetNodeByID(id);
+ DCHECK(node);
+ if (node) {
+ model->SetTitle(node, WideToUTF16(title));
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+void AutomationProvider::SetBookmarkURL(int handle,
+ int64 id, const GURL& url,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* node = model->GetNodeByID(id);
+ DCHECK(node);
+ if (node) {
+ model->SetURL(node, url);
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
+void AutomationProvider::RemoveBookmark(int handle,
+ int64 id,
+ bool* success) {
+ if (browser_tracker_->ContainsHandle(handle)) {
+ Browser* browser = browser_tracker_->GetResource(handle);
+ if (browser) {
+ BookmarkModel* model = browser->profile()->GetBookmarkModel();
+ if (!model->IsLoaded()) {
+ *success = false;
+ return;
+ }
+ const BookmarkNode* node = model->GetNodeByID(id);
+ DCHECK(node);
+ if (node) {
+ const BookmarkNode* parent = node->GetParent();
+ DCHECK(parent);
+ model->Remove(parent, parent->IndexOfChild(node));
+ *success = true;
+ }
+ }
+ }
+ *success = false;
+}
+
void AutomationProvider::HandleInspectElementRequest(
int handle, int x, int y, IPC::Message* reply_message) {
TabContents* tab_contents = GetTabContentsForHandle(handle, NULL);
diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h
index 6d08f38..88bb865 100644
--- a/chrome/browser/automation/automation_provider.h
+++ b/chrome/browser/automation/automation_provider.h
@@ -285,6 +285,38 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
// Get the visibility state of the Bookmark bar.
void GetBookmarkBarVisibility(int handle, bool* visible, bool* animating);
+ // Get the bookmarks as a JSON string.
+ void GetBookmarksAsJSON(int handle, std::string* bookmarks_as_json,
+ bool *success);
+
+ // Wait for the bookmark model to load.
+ void WaitForBookmarkModelToLoad(int handle, IPC::Message* reply_message);
+
+ // Set |loaded| to true if the bookmark model has loaded, else false.
+ void BookmarkModelHasLoaded(int handle, bool* loaded);
+
+ // Editing, modification, and removal of bookmarks.
+ // Bookmarks are referenced by id.
+ void AddBookmarkGroup(int handle,
+ int64 parent_id, int index, std::wstring title,
+ bool* success);
+ void AddBookmarkURL(int handle,
+ int64 parent_id, int index,
+ std::wstring title, const GURL& url,
+ bool* success);
+ void ReparentBookmark(int handle,
+ int64 id, int64 new_parent_id, int index,
+ bool* success);
+ void SetBookmarkTitle(int handle,
+ int64 id, std::wstring title,
+ bool* success);
+ void SetBookmarkURL(int handle,
+ int64 id, const GURL& url,
+ bool* success);
+ void RemoveBookmark(int handle,
+ int64 id,
+ bool* success);
+
// Responds to InspectElement request
void HandleInspectElementRequest(int handle,
int x,
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc
index 9c5f656..7f1f4f5 100644
--- a/chrome/browser/automation/automation_provider_observers.cc
+++ b/chrome/browser/automation/automation_provider_observers.cc
@@ -7,6 +7,7 @@
#include "base/basictypes.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/automation/automation_provider.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/dom_operation_notification_details.h"
#include "chrome/browser/login_prompt.h"
#include "chrome/browser/metrics/metric_event_duration_details.h"
@@ -743,3 +744,24 @@ void LoginManagerObserver::Observe(NotificationType type,
delete this;
}
#endif
+
+AutomationProviderBookmarkModelObserver::AutomationProviderBookmarkModelObserver(
+ AutomationProvider* provider,
+ IPC::Message* reply_message,
+ BookmarkModel* model) {
+ automation_provider_ = provider;
+ reply_message_ = reply_message;
+ model_ = model;
+ model_->AddObserver(this);
+}
+
+AutomationProviderBookmarkModelObserver::~AutomationProviderBookmarkModelObserver() {
+ model_->RemoveObserver(this);
+}
+
+void AutomationProviderBookmarkModelObserver::ReplyAndDelete(bool success) {
+ AutomationMsg_WaitForBookmarkModelToLoad::WriteReplyParams(
+ reply_message_, success);
+ automation_provider_->Send(reply_message_);
+ delete this;
+}
diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h
index 79c824d..0d75e70 100644
--- a/chrome/browser/automation/automation_provider_observers.h
+++ b/chrome/browser/automation/automation_provider_observers.h
@@ -8,6 +8,7 @@
#include <map>
#include <set>
+#include "chrome/browser/bookmarks/bookmark_model_observer.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_type.h"
@@ -405,4 +406,50 @@ class LoginManagerObserver : public NotificationObserver {
};
#endif
+// Waits for the bookmark model to load.
+class AutomationProviderBookmarkModelObserver : BookmarkModelObserver {
+ public:
+ AutomationProviderBookmarkModelObserver(AutomationProvider* provider,
+ IPC::Message* reply_message,
+ BookmarkModel* model);
+ virtual ~AutomationProviderBookmarkModelObserver();
+
+ virtual void Loaded(BookmarkModel* model) {
+ ReplyAndDelete(true);
+ }
+ virtual void BookmarkModelBeingDeleted(BookmarkModel* model) {
+ ReplyAndDelete(false);
+ }
+ virtual void BookmarkNodeMoved(BookmarkModel* model,
+ const BookmarkNode* old_parent,
+ int old_index,
+ const BookmarkNode* new_parent,
+ int new_index) {}
+ virtual void BookmarkNodeAdded(BookmarkModel* model,
+ const BookmarkNode* parent,
+ int index) {}
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ const BookmarkNode* parent,
+ int old_index,
+ const BookmarkNode* node) {}
+ virtual void BookmarkNodeChanged(BookmarkModel* model,
+ const BookmarkNode* node) {}
+ virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
+ const BookmarkNode* node) {}
+ virtual void BookmarkNodeChildrenReordered(BookmarkModel* model,
+ const BookmarkNode* node) {}
+
+ private:
+ // Reply to the automation message with the given success value,
+ // then delete myself (which removes myself from the bookmark model
+ // observer list).
+ void ReplyAndDelete(bool success);
+
+ scoped_refptr<AutomationProvider> automation_provider_;
+ IPC::Message* reply_message_;
+ BookmarkModel* model_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutomationProviderBookmarkModelObserver);
+};
+
#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_OBSERVERS_H_
diff --git a/chrome/test/automation/automation_messages_internal.h b/chrome/test/automation/automation_messages_internal.h
index 26aabfc..c01df68 100644
--- a/chrome/test/automation/automation_messages_internal.h
+++ b/chrome/test/automation/automation_messages_internal.h
@@ -1230,4 +1230,51 @@ IPC_BEGIN_MESSAGES(Automation)
bool /* Whether successful*/)
#endif
+ // Return the bookmarks encoded as a JSON string.
+ IPC_SYNC_MESSAGE_ROUTED1_2(AutomationMsg_GetBookmarksAsJSON,
+ int /* browser_handle */,
+ std::string /* bookmarks as a JSON string */,
+ bool /* success */)
+
+ // Wait for the bookmark model to load.
+ IPC_SYNC_MESSAGE_ROUTED1_1(AutomationMsg_WaitForBookmarkModelToLoad,
+ int /* browser_handle */,
+ bool /* success */)
+
+ // Bookmark addition, modification, and removal.
+ // Bookmarks are indexed by their id.
+ IPC_SYNC_MESSAGE_ROUTED4_1(AutomationMsg_AddBookmarkGroup,
+ int /* browser_handle */,
+ int64 /* parent_id */,
+ int /* index */,
+ std::wstring /* title */,
+ bool /* success */)
+ IPC_SYNC_MESSAGE_ROUTED5_1(AutomationMsg_AddBookmarkURL,
+ int /* browser_handle */,
+ int64 /* parent_id */,
+ int /* index */,
+ std::wstring /* title */,
+ GURL /* url */,
+ bool /* success */)
+ IPC_SYNC_MESSAGE_ROUTED4_1(AutomationMsg_ReparentBookmark,
+ int /* browser_handle */,
+ int64 /* id */,
+ int64 /* new_parent_id */,
+ int /* index */,
+ bool /* success */)
+ IPC_SYNC_MESSAGE_ROUTED3_1(AutomationMsg_SetBookmarkTitle,
+ int /* browser_handle */,
+ int64 /* id */,
+ std::wstring /* title */,
+ bool /* success */)
+ IPC_SYNC_MESSAGE_ROUTED3_1(AutomationMsg_SetBookmarkURL,
+ int /* browser_handle */,
+ int64 /* id */,
+ GURL /* url */,
+ bool /* success */)
+ IPC_SYNC_MESSAGE_ROUTED2_1(AutomationMsg_RemoveBookmark,
+ int /* browser_handle */,
+ int64 /* id */,
+ bool /* success */)
+
IPC_END_MESSAGES(Automation)
diff --git a/chrome/test/automation/browser_proxy.cc b/chrome/test/automation/browser_proxy.cc
index 2b62b71..2411dd6 100644
--- a/chrome/test/automation/browser_proxy.cc
+++ b/chrome/test/automation/browser_proxy.cc
@@ -327,6 +327,95 @@ bool BrowserProxy::GetBookmarkBarVisibility(bool* is_visible,
0, handle_, is_visible, is_animating));
}
+bool BrowserProxy::GetBookmarksAsJSON(std::string *json_string) {
+ if (!is_valid())
+ return false;
+
+ if (!WaitForBookmarkModelToLoad())
+ return false;
+
+ bool result = false;
+ sender_->Send(new AutomationMsg_GetBookmarksAsJSON(0, handle_,
+ json_string,
+ &result));
+ return result;
+}
+
+bool BrowserProxy::WaitForBookmarkModelToLoad() {
+ if (!is_valid())
+ return false;
+
+ bool result = false;
+ sender_->Send(new AutomationMsg_WaitForBookmarkModelToLoad(0, handle_,
+ &result));
+ return result;
+}
+
+bool BrowserProxy::AddBookmarkGroup(int64 parent_id, int index,
+ std::wstring& title) {
+ if (!is_valid())
+ return false;
+ bool result = false;
+ sender_->Send(new AutomationMsg_AddBookmarkGroup(0, handle_,
+ parent_id, index,
+ title,
+ &result));
+ return result;
+}
+
+bool BrowserProxy::AddBookmarkURL(int64 parent_id, int index,
+ std::wstring& title, const GURL& url) {
+ if (!is_valid())
+ return false;
+ bool result = false;
+ sender_->Send(new AutomationMsg_AddBookmarkURL(0, handle_,
+ parent_id, index,
+ title, url,
+ &result));
+ return result;
+}
+
+bool BrowserProxy::ReparentBookmark(int64 id, int64 new_parent_id, int index) {
+ if (!is_valid())
+ return false;
+ bool result = false;
+ sender_->Send(new AutomationMsg_ReparentBookmark(0, handle_,
+ id, new_parent_id,
+ index,
+ &result));
+ return result;
+}
+
+bool BrowserProxy::SetBookmarkTitle(int64 id, std::wstring& title) {
+ if (!is_valid())
+ return false;
+ bool result = false;
+ sender_->Send(new AutomationMsg_SetBookmarkTitle(0, handle_,
+ id, title,
+ &result));
+ return result;
+}
+
+bool BrowserProxy::SetBookmarkURL(int64 id, const GURL& url) {
+ if (!is_valid())
+ return false;
+ bool result = false;
+ sender_->Send(new AutomationMsg_SetBookmarkURL(0, handle_,
+ id, url,
+ &result));
+ return result;
+}
+
+bool BrowserProxy::RemoveBookmark(int64 id) {
+ if (!is_valid())
+ return false;
+ bool result = false;
+ sender_->Send(new AutomationMsg_RemoveBookmark(0, handle_,
+ id,
+ &result));
+ return result;
+}
+
bool BrowserProxy::IsShelfVisible(bool* is_visible) {
if (!is_valid())
return false;
diff --git a/chrome/test/automation/browser_proxy.h b/chrome/test/automation/browser_proxy.h
index 939e87b..04e6288 100644
--- a/chrome/test/automation/browser_proxy.h
+++ b/chrome/test/automation/browser_proxy.h
@@ -181,6 +181,25 @@ class BrowserProxy : public AutomationResourceProxy {
// it into position. Returns false on failure.
bool GetBookmarkBarVisibility(bool* is_visible, bool* is_animating);
+ // Get the bookmarks as a JSON string and put it in |json_string|.
+ // Return true on success.
+ bool GetBookmarksAsJSON(std::string* json_string);
+
+ // Wait for the bookmarks to load. Called implicitly by GetBookmarksAsJSON().
+ bool WaitForBookmarkModelToLoad();
+
+ // Editing of the bookmark model. Bookmarks are referenced by id.
+ // Bookmark or group (folder) creation:
+ bool AddBookmarkGroup(int64 parent_id, int index, std::wstring& title);
+ bool AddBookmarkURL(int64 parent_id, int index,
+ std::wstring& title, const GURL& url);
+ // Bookmark editing:
+ bool ReparentBookmark(int64 id, int64 new_parent_id, int index);
+ bool SetBookmarkTitle(int64 id, std::wstring& title);
+ bool SetBookmarkURL(int64 id, const GURL& url);
+ // Finally, bookmark deletion:
+ bool RemoveBookmark(int64 id);
+
// Fills |*is_visible| with whether the browser's download shelf is currently
// visible. The return value indicates success. On failure, |*is_visible| is
// unchanged.
diff --git a/chrome/test/functional/bookmarks.py b/chrome/test/functional/bookmarks.py
new file mode 100644
index 0000000..7f28920
--- /dev/null
+++ b/chrome/test/functional/bookmarks.py
@@ -0,0 +1,118 @@
+#!/usr/bin/python
+# Copyright (c) 2010 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Must import this first
+import pyauto_test_utils
+
+import unittest
+
+import pyauto
+
+
+class BookmarksTest(pyauto.PyUITest):
+ """Test of bookmarks."""
+
+ def testBasics(self):
+ """Basic tests with an empty bookmark model."""
+ bookmarks = self.GetBookmarkModel()
+ # Make sure we have the two root nodes and that they are empty
+ for node in (bookmarks.BookmarkBar(), bookmarks.Other()):
+ self.assertEqual(node['type'], 'folder')
+ self.assertFalse(node['children'])
+
+ def testAddOneNode(self):
+ """Add a bookmark to the bar; confirm it."""
+ bookmarks = self.GetBookmarkModel()
+ bar_id = bookmarks.BookmarkBar()['id']
+ name = 'Google'
+ url = 'http://www.google.com'
+ c = bookmarks.NodeCount()
+ self.AddBookmarkURL(bar_id, 0, name, url)
+ bookmarks = self.GetBookmarkModel()
+ node = bookmarks.BookmarkBar()['children'][0]
+ self.assertEqual(c+1, bookmarks.NodeCount())
+ self.assertEqual(node['type'], 'url')
+ self.assertEqual(node['name'], name)
+ # URLs may not be exact; e.g. http://www.com --> http://www.com/
+ self.assertTrue(url in node['url'])
+ # Make sure we can search and find the same thing
+ nodes = bookmarks.FindByTitle(name)
+ self.assertEqual(1, len(nodes))
+ self.assertTrue(nodes[0]['id'] == node['id'])
+
+ def testAddGroup(self):
+ """Add a group to the bar; confirm it."""
+ bookmarks = self.GetBookmarkModel()
+ c = bookmarks.NodeCount()
+ parent_id = bookmarks.Other()['id']
+ name = 'SuperGroup'
+ self.AddBookmarkGroup(parent_id, 0, name)
+ # Confirm group.
+ bookmarks = self.GetBookmarkModel()
+ node = bookmarks.Other()['children'][0]
+ self.assertEqual(c+1, bookmarks.NodeCount())
+ self.assertEqual(node['type'], 'folder')
+ self.assertEqual(node['name'], name)
+ # Make sure we can search and find the same thing
+ findnode = bookmarks.FindByID(node['id'])
+ self.assertEqual(node, findnode)
+
+ def testAddChangeRemove(self):
+ """Add some bookmarks. Change their title and URL. Remove one."""
+ # Add some.
+ bookmarks = self.GetBookmarkModel()
+ bar_id = bookmarks.BookmarkBar()['id']
+ self.AddBookmarkURL(bar_id, 0, 'Title1',
+ 'http://www.google.com')
+ self.AddBookmarkURL(bar_id, 1, 'Title1',
+ 'http://www.google.com/reader')
+ # Change a title and URL.
+ bookmarks = self.GetBookmarkModel()
+ nodes = bookmarks.FindByTitle('Title1')
+ self.assertEqual(2, len(nodes))
+ self.SetBookmarkTitle(nodes[0]['id'], 'Title2')
+ self.SetBookmarkURL(nodes[1]['id'], 'http://www.youtube.com')
+ # Confirm, then remove.
+ bookmarks = self.GetBookmarkModel()
+ nodes = bookmarks.FindByTitle('Title1')
+ self.assertEqual(1, len(nodes))
+ self.assertTrue('http://www.youtube.com' in nodes[0]['url'])
+ nodes = bookmarks.FindByTitle('Title2')
+ self.assertEqual(1, len(nodes))
+ self.assertTrue('google.com' in nodes[0]['url'])
+ self.RemoveBookmark(nodes[0]['id'])
+ # Confirm removal.
+ bookmarks = self.GetBookmarkModel()
+ nodes = bookmarks.FindByTitle('Title2')
+ self.assertEqual(0, len(nodes))
+
+ def testReparent(self):
+ bookmarks = self.GetBookmarkModel()
+ bar_id = bookmarks.BookmarkBar()['id']
+ # Add some groups
+ for i in range(3):
+ self.AddBookmarkGroup(bar_id, i, 'Group' + str(i))
+ # Add a bookmark in one group
+ bookmarks = self.GetBookmarkModel()
+ nodes = bookmarks.FindByTitle('Group0')
+ self.AddBookmarkURL(nodes[0]['id'], 0,
+ 'marked', 'http://www.youtube.com')
+ # Make sure it's not in a different group
+ bookmarks = self.GetBookmarkModel()
+ nodes = bookmarks.FindByTitle('Group2')
+ self.assertFalse(nodes[0]['children'])
+ # Move it to that group
+ self.ReparentBookmark(bookmarks.FindByTitle('marked')[0]['id'],
+ bookmarks.FindByTitle('Group2')[0]['id'], 0)
+ # Confirm.
+ bookmarks = self.GetBookmarkModel()
+ nodes = bookmarks.FindByTitle('Group0')
+ self.assertEqual([], nodes[0]['children'])
+ nodes = bookmarks.FindByTitle('Group2')
+ self.assertEqual(1, len(nodes[0]['children']))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/chrome/test/functional/pyauto_test_utils.py b/chrome/test/functional/pyauto_test_utils.py
new file mode 100644
index 0000000..72aedf9
--- /dev/null
+++ b/chrome/test/functional/pyauto_test_utils.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+# Copyright (c) 2010 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Simple utilities to assist with pyauto test scripts.
+
+Assumes the test scripts live in chrome/test/functional.
+"""
+
+import os
+import sys
+
+# Add path to simplejson
+sys.path.append(os.path.join(os.path.dirname(__file__),
+ '..', '..', '..',
+ 'third_party'))
+
+# Add path to pyauto
+sys.path.append(os.path.join(os.path.dirname(__file__),
+ '..',
+ 'pyautolib'))
diff --git a/chrome/test/pyautolib/bookmark_model.py b/chrome/test/pyautolib/bookmark_model.py
new file mode 100644
index 0000000..cc43f73
--- /dev/null
+++ b/chrome/test/pyautolib/bookmark_model.py
@@ -0,0 +1,105 @@
+#!/usr/bin/python
+
+# Copyright (c) 2010 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""BookmarkModel: python representation of the bookmark model.
+
+Obtain one of these from PyUITestSuite::GetBookmarkModel() call.
+"""
+
+# TODO(jrg): establish a standard path for pyauto tests.
+# This "adjustment" assumes we live in "src/chrome/test/pyautolib".
+import os
+import sys
+sys.path.append(os.path.join(os.path.dirname(__file__), 'third_party'))
+
+import simplejson as json
+
+class BookmarkModel(object):
+
+ def __init__(self, json_string):
+ """Initialize a BookmarkModel from a string of json.
+
+ The JSON representation is the same as used by the bookmark model
+ to save to disk.
+
+ Args:
+ json_string: a string of JSON.
+ """
+ self.bookdict = json.loads(json_string)
+
+ def BookmarkBar(self):
+ """Return the bookmark bar node as a dict."""
+ return self.bookdict['roots']['bookmark_bar']
+
+ def Other(self):
+ """Return the 'other' node (e.g. parent of "Other Bookmarks")"""
+ return self.bookdict['roots']['other']
+
+ def NodeCount(self, node=None):
+ """Return a count of bookmark nodes, including folders.
+
+ The root node itself is included in the count.
+
+ Args:
+ node: the root to start with. If not specified, count all."""
+ if node == None:
+ return reduce(lambda x, y: x + y,
+ [self.NodeCount(x)
+ for x in self.bookdict['roots'].values()])
+ total = 1
+ children = node.get('children', None)
+ if children:
+ total = total + reduce(lambda x,y: x + y,
+ [self.NodeCount(x) for x in children])
+ return total
+
+ def FindByID(self, id, nodes=None):
+ """Find the bookmark by id. Return the dict or None.
+
+ Args:
+ id: the id to look for.
+ nodes: an iterable of nodes to start with. If not specified, search all.
+ 'Not specified' means None, not [].
+ """
+ # Careful; we may get an empty list which is different than not
+ # having specified a list.
+ if nodes == None:
+ nodes = self.bookdict['roots'].values()
+ # Check each item. If it matches, return. If not, check each of
+ # their kids.
+ for node in nodes:
+ if node['id'] == id:
+ return node
+ for child in node['children']:
+ found_node = self.FindByID(id, [child])
+ if found_node:
+ return found_node
+ # Not found at all.
+ return None
+
+ def FindByTitle(self, title, nodes=None):
+ """Return a tuple of all nodes which have |title| in their title.
+
+ Args:
+ title: the title to look for.
+ node: an iterable of nodes to start with. If not specified, search all.
+ 'Not specified' means None, not [].
+ """
+ # Careful; we may get an empty list which is different than not
+ # having specified a list.
+ if nodes == None:
+ nodes = self.bookdict['roots'].values()
+ # Check each item. If it matches, return. If not, check each of
+ # their kids.
+ results = []
+ for node in nodes:
+ node_title = node.get('title', None) or node.get('name', None)
+ if title == node_title:
+ results.append(node)
+ # Note we check everything; unlike the FindByID, we do not stop early.
+ for child in node.get('children', []):
+ results += self.FindByTitle(title, [child])
+ return results
diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py
index 8e1db78..d910489 100644
--- a/chrome/test/pyautolib/pyauto.py
+++ b/chrome/test/pyautolib/pyauto.py
@@ -17,6 +17,8 @@ import os
import sys
import unittest
+import bookmark_model
+
def _LocateBinDirs():
script_dir = os.path.dirname(__file__)
@@ -98,3 +100,11 @@ class PyUITest(pyautolib.PyUITestSuite, unittest.TestCase):
self.SetUp() # Open a browser window
unittest.TestCase.run(self, result)
self.TearDown() # Destroy the browser window
+
+ def GetBookmarkModel(self):
+ """Return the bookmark model as a BookmarkModel object.
+
+ This is a snapshot of the bookmark model; it is not a proxy and
+ does not get updated as the bookmark model changes.
+ """
+ return bookmark_model.BookmarkModel(self._GetBookmarksAsJSON())
diff --git a/chrome/test/pyautolib/pyautolib.cc b/chrome/test/pyautolib/pyautolib.cc
index b9090e4..f009549 100644
--- a/chrome/test/pyautolib/pyautolib.cc
+++ b/chrome/test/pyautolib/pyautolib.cc
@@ -3,6 +3,8 @@
// found in the LICENSE file.
#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/pyautolib/pyautolib.h"
#include "googleurl/src/gurl.h"
@@ -158,3 +160,87 @@ bool PyUITestSuite::WaitForBookmarkBarVisibilityChange(bool wait_for_open) {
return completed;
}
+std::string PyUITestSuite::_GetBookmarksAsJSON() {
+ scoped_refptr<BrowserProxy> browser_proxy =
+ automation()->GetBrowserWindow(0); // Window doesn't matter.
+ EXPECT_TRUE(browser_proxy.get());
+ if (!browser_proxy.get())
+ return false;
+
+ std::string s;
+ EXPECT_TRUE(browser_proxy->GetBookmarksAsJSON(&s));
+ return s;
+}
+
+bool PyUITestSuite::AddBookmarkGroup(std::wstring& parent_id, int index,
+ std::wstring& title) {
+ scoped_refptr<BrowserProxy> browser_proxy =
+ automation()->GetBrowserWindow(0); // Window doesn't matter.
+ EXPECT_TRUE(browser_proxy.get());
+ if (!browser_proxy.get())
+ return false;
+
+ return browser_proxy->AddBookmarkGroup(StringToInt64(WideToUTF16(parent_id)),
+ index, title);
+}
+
+bool PyUITestSuite::AddBookmarkURL(std::wstring& parent_id, int index,
+ std::wstring& title, std::wstring& url) {
+ scoped_refptr<BrowserProxy> browser_proxy =
+ automation()->GetBrowserWindow(0); // Window doesn't matter.
+ EXPECT_TRUE(browser_proxy.get());
+ if (!browser_proxy.get())
+ return false;
+
+ return browser_proxy->AddBookmarkURL(StringToInt64(WideToUTF16(parent_id)),
+ index, title,
+ GURL(WideToUTF16(url)));
+}
+
+bool PyUITestSuite::ReparentBookmark(std::wstring& id, std::wstring& new_parent_id,
+ int index) {
+ scoped_refptr<BrowserProxy> browser_proxy =
+ automation()->GetBrowserWindow(0); // Window doesn't matter.
+ EXPECT_TRUE(browser_proxy.get());
+ if (!browser_proxy.get())
+ return false;
+
+ return browser_proxy->ReparentBookmark(
+ StringToInt64(WideToUTF16(id)),
+ StringToInt64(WideToUTF16(new_parent_id)),
+ index);
+}
+
+bool PyUITestSuite::SetBookmarkTitle(std::wstring& id, std::wstring& title) {
+ scoped_refptr<BrowserProxy> browser_proxy =
+ automation()->GetBrowserWindow(0); // Window doesn't matter.
+ EXPECT_TRUE(browser_proxy.get());
+ if (!browser_proxy.get())
+ return false;
+
+ return browser_proxy->SetBookmarkTitle(StringToInt64(WideToUTF16(id)),
+ title);
+}
+
+bool PyUITestSuite::SetBookmarkURL(std::wstring& id, std::wstring& url) {
+ scoped_refptr<BrowserProxy> browser_proxy =
+ automation()->GetBrowserWindow(0); // Window doesn't matter.
+ EXPECT_TRUE(browser_proxy.get());
+ if (!browser_proxy.get())
+ return false;
+
+ return browser_proxy->SetBookmarkURL(StringToInt64(WideToUTF16(id)),
+ GURL(WideToUTF16(url)));
+}
+
+bool PyUITestSuite::RemoveBookmark(std::wstring& id) {
+ scoped_refptr<BrowserProxy> browser_proxy =
+ automation()->GetBrowserWindow(0); // Window doesn't matter.
+ EXPECT_TRUE(browser_proxy.get());
+ if (!browser_proxy.get())
+ return false;
+
+ return browser_proxy->RemoveBookmark(StringToInt64(WideToUTF16(id)));
+}
+
+
diff --git a/chrome/test/pyautolib/pyautolib.h b/chrome/test/pyautolib/pyautolib.h
index 461834a..154c97c 100644
--- a/chrome/test/pyautolib/pyautolib.h
+++ b/chrome/test/pyautolib/pyautolib.h
@@ -102,6 +102,28 @@ class PyUITestSuite : public UITestSuite, public UITestBase {
// If |wait_for_open| is false, wait for it to close.
bool WaitForBookmarkBarVisibilityChange(bool wait_for_open);
+ // Get the bookmarks as a JSON string. Internal method.
+ std::string _GetBookmarksAsJSON();
+
+ // Editing of the bookmark model. Bookmarks are referenced by id.
+ // The id is a std::wstring, not an int64, for convenience, since
+ // the python side gets IDs converted from a JSON representation
+ // (which "extracts" into a string, not an int). Since IDs are
+ // grabbed from the current model (and not generated), a conversion
+ // is unnecessary. URLs are strings and not GURLs for a similar reason.
+ // Bookmark or group (folder) creation:
+ bool AddBookmarkGroup(std::wstring& parent_id, int index,
+ std::wstring& title);
+ bool AddBookmarkURL(std::wstring& parent_id, int index,
+ std::wstring& title, std::wstring& url);
+ // Bookmark editing:
+ bool ReparentBookmark(std::wstring& id, std::wstring& new_parent_id,
+ int index);
+ bool SetBookmarkTitle(std::wstring& id, std::wstring& title);
+ bool SetBookmarkURL(std::wstring& id, std::wstring& url);
+ // Finally, bookmark deletion:
+ bool RemoveBookmark(std::wstring& id);
+
private:
base::ScopedNSAutoreleasePool pool_;
};
diff --git a/chrome/test/pyautolib/pyautolib.i b/chrome/test/pyautolib/pyautolib.i
index f1a01f1..e181a71 100644
--- a/chrome/test/pyautolib/pyautolib.i
+++ b/chrome/test/pyautolib/pyautolib.i
@@ -140,6 +140,30 @@ class PyUITestSuite {
WaitForBookmarkBarVisibilityChange;
bool WaitForBookmarkBarVisibilityChange(bool wait_for_open);
+ %feature("docstring", "Get the bookmarks as a JSON string. Internal method.")
+ _GetBookmarksAsJSON;
+ std::string _GetBookmarksAsJSON();
+
+ %feature("docstring", "Add a bookmark folder with the given index in the parent."
+ " |title| is the title/name of the folder.") AddBookmarkGroup;
+ bool AddBookmarkGroup(std::wstring parent_id, int index, std::wstring title);
+
+ %feature("docstring", "Add a bookmark with the given title and URL.") AddBookmarkURL;
+ bool AddBookmarkURL(std::wstring parent_id, int index,
+ std::wstring title, const std::wstring url);
+
+ %feature("docstring", "Move a bookmark to a new parent.") ReparentBookmark;
+ bool ReparentBookmark(std::wstring id, std::wstring new_parent_id, int index);
+
+ %feature("docstring", "Set the title of a bookmark.") SetBookmarkTitle;
+ bool SetBookmarkTitle(std::wstring id, std::wstring title);
+
+ %feature("docstring", "Set the URL of a bookmark.") SetBookmarkURL;
+ bool SetBookmarkURL(std::wstring id, const std::wstring url);
+
+ %feature("docstring", "Remove (delete) a bookmark.") RemoveBookmark;
+ bool RemoveBookmark(std::wstring id);
+
%feature("docstring", "Open the Find box in the given or first browser "
"window.") OpenFindInPage;
void OpenFindInPage(int window_index=0);
diff --git a/ipc/ipc_message_macros.h b/ipc/ipc_message_macros.h
index 6346e8c..d14f545 100644
--- a/ipc/ipc_message_macros.h
+++ b/ipc/ipc_message_macros.h
@@ -133,6 +133,10 @@
#undef IPC_SYNC_MESSAGE_ROUTED4_1
#undef IPC_SYNC_MESSAGE_ROUTED4_2
#undef IPC_SYNC_MESSAGE_ROUTED4_3
+#undef IPC_SYNC_MESSAGE_ROUTED5_0
+#undef IPC_SYNC_MESSAGE_ROUTED5_1
+#undef IPC_SYNC_MESSAGE_ROUTED5_2
+#undef IPC_SYNC_MESSAGE_ROUTED5_3
#if defined(IPC_MESSAGE_MACROS_ENUMS)
#undef IPC_MESSAGE_MACROS_ENUMS
@@ -300,6 +304,18 @@
#define IPC_SYNC_MESSAGE_ROUTED4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \
msg_class##__ID,
+#define IPC_SYNC_MESSAGE_ROUTED5_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED5_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED5_2(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out) \
+ msg_class##__ID,
+
+#define IPC_SYNC_MESSAGE_ROUTED5_3(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out) \
+ msg_class##__ID,
+
// Message crackers and handlers.
// Prefer to use the IPC_BEGIN_MESSAGE_MAP_EX to the older macros since they
// allow you to detect when a message could not be de-serialized. Usage:
@@ -570,6 +586,18 @@ LogFunction g_log_function_mapping[LastMsgIndex];
#define IPC_SYNC_MESSAGE_ROUTED4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \
IPC_MESSAGE_LOG(msg_class)
+#define IPC_SYNC_MESSAGE_ROUTED5_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED5_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED5_2(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
+#define IPC_SYNC_MESSAGE_ROUTED5_3(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out) \
+ IPC_MESSAGE_LOG(msg_class)
+
#elif defined(IPC_MESSAGE_MACROS_CLASSES)
#undef IPC_MESSAGE_MACROS_CLASSES
@@ -1145,4 +1173,52 @@ LogFunction g_log_function_mapping[LastMsgIndex];
MakeRefTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg5, *arg6, *arg7)) {} \
};
+#define IPC_SYNC_MESSAGE_ROUTED5_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple5<type1_in, type2_in, type3_in, type4_in, type5_in>, \
+ Tuple0 > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, const type5_in& arg5) \
+ : IPC::MessageWithReply<Tuple5<type1_in, type2_in, type3_in, type4_in, type5_in>, \
+ Tuple0 >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4, arg5), MakeTuple()) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED5_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple5<type1_in, type2_in, type3_in, type4_in, type5_in>, \
+ Tuple1<type1_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, const type5_in& arg5, type1_out* arg6) \
+ : IPC::MessageWithReply<Tuple5<type1_in, type2_in, type3_in, type4_in, type5_in>, \
+ Tuple1<type1_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4, arg5), MakeRefTuple(*arg6)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED5_2(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple5<type1_in, type2_in, type3_in, type4_in, type5_in>, \
+ Tuple2<type1_out&, type2_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, const type4_in& arg5, type1_out* arg6, type2_out* arg7) \
+ : IPC::MessageWithReply<Tuple5<type1_in, type2_in, type3_in, type4_in, type5_in>, \
+ Tuple2<type1_out&, type2_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4, arg5), MakeRefTuple(*arg6, *arg7)) {} \
+ };
+
+#define IPC_SYNC_MESSAGE_ROUTED5_3(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out) \
+ class msg_class : \
+ public IPC::MessageWithReply<Tuple5<type1_in, type2_in, type3_in, type4_in, type5_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> > { \
+ public: \
+ enum { ID = msg_class##__ID }; \
+ msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, const type4_in& arg5, type1_out* arg6, type2_out* arg7, type3_out* arg8) \
+ : IPC::MessageWithReply<Tuple5<type1_in, type2_in, type3_in, type4_in, type5_in>, \
+ Tuple3<type1_out&, type2_out&, type3_out&> >(routing_id, ID, \
+ MakeRefTuple(arg1, arg2, arg3, arg4, arg5), MakeRefTuple(*arg6, *arg7, *arg8)) {} \
+ };
+
#endif // #if defined()