summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/automation/ui_controls.cc228
-rw-r--r--chrome/browser/automation/ui_controls.h28
-rw-r--r--chrome/browser/bookmark_bar_model.cc25
-rw-r--r--chrome/browser/bookmark_bar_model.h6
-rw-r--r--chrome/browser/views/bookmark_bar_view.cc65
-rw-r--r--chrome/browser/views/bookmark_bar_view.h33
-rw-r--r--chrome/browser/views/bookmark_bar_view_test.cc857
-rw-r--r--chrome/test/testing_profile.cc4
-rw-r--r--chrome/test/testing_profile.h23
-rw-r--r--chrome/test/ui/view_event_test_base.cc135
-rw-r--r--chrome/test/ui/view_event_test_base.h160
-rw-r--r--chrome/test/unit/unittests.vcproj16
-rw-r--r--chrome/views/chrome_menu.cc13
-rw-r--r--chrome/views/chrome_menu.h6
-rw-r--r--chrome/views/menu_button.cc2
15 files changed, 1536 insertions, 65 deletions
diff --git a/chrome/browser/automation/ui_controls.cc b/chrome/browser/automation/ui_controls.cc
index 811bd89..f7c7806 100644
--- a/chrome/browser/automation/ui_controls.cc
+++ b/chrome/browser/automation/ui_controls.cc
@@ -30,8 +30,131 @@
#include "chrome/browser/automation/ui_controls.h"
#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/task.h"
+#include "chrome/views/view.h"
-// Methods private to this file
+namespace ui_controls {
+
+namespace {
+
+// InputDispatcher ------------------------------------------------------------
+
+// InputDispatcher is used to listen for a mouse/keyboard event. When the
+// appropriate event is received the task is notified.
+class InputDispatcher : public base::RefCounted<InputDispatcher> {
+ public:
+ InputDispatcher(Task* task, WPARAM message_waiting_for);
+
+ ~InputDispatcher();
+
+ // Invoked from the hook. If mouse_message matches message_waiting_for_
+ // MatchingMessageFound is invoked.
+ void DispatchedMessage(WPARAM mouse_message);
+
+ // Invoked when a matching event is found. Uninstalls the hook and schedules
+ // an event that notifies the task.
+ void MatchingMessageFound();
+
+ private:
+ // Notifies the task and release this (which should delete it).
+ void NotifyTask();
+
+ // The task we notify.
+ scoped_ptr<Task> task_;
+
+ // Message we're waiting for. Not used for keyboard events.
+ const WPARAM message_waiting_for_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputDispatcher);
+};
+
+// Have we installed the hook?
+bool installed_hook_ = false;
+
+// Return value from SetWindowsHookEx.
+HHOOK next_hook_ = NULL;
+
+// If a hook is installed, this is the dispatcher.
+InputDispatcher* current_dispatcher_ = NULL;
+
+// Callback from hook when a mouse message is received.
+LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param) {
+ HHOOK next_hook = next_hook_;
+ if (n_code == HC_ACTION) {
+ DCHECK(current_dispatcher_);
+ current_dispatcher_->DispatchedMessage(w_param);
+ }
+ return CallNextHookEx(next_hook, n_code, w_param, l_param);
+}
+
+// Callback from hook when a key message is received.
+LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param) {
+ HHOOK next_hook = next_hook_;
+ if (n_code == HC_ACTION) {
+ DCHECK(current_dispatcher_);
+ if (l_param & (1 << 30)) // Only send on key up.
+ current_dispatcher_->MatchingMessageFound();
+ }
+ return CallNextHookEx(next_hook, n_code, w_param, l_param);
+}
+
+// Installs dispatcher as the current hook.
+void InstallHook(InputDispatcher* dispatcher, bool key_hook) {
+ DCHECK(!installed_hook_);
+ current_dispatcher_ = dispatcher;
+ installed_hook_ = true;
+ if (key_hook) {
+ next_hook_ = SetWindowsHookEx(WH_KEYBOARD, &KeyHook, NULL,
+ GetCurrentThreadId());
+ } else {
+ // NOTE: I originally tried WH_CALLWNDPROCRET, but for some reason I
+ // didn't get a mouse message like I do with MouseHook.
+ next_hook_ = SetWindowsHookEx(WH_MOUSE, &MouseHook, NULL,
+ GetCurrentThreadId());
+ }
+ DCHECK(next_hook_);
+}
+
+// Uninstalls the hook set in InstallHook.
+void UninstallHook(InputDispatcher* dispatcher) {
+ if (current_dispatcher_ == dispatcher) {
+ installed_hook_ = false;
+ current_dispatcher_ = NULL;
+ UnhookWindowsHookEx(next_hook_);
+ }
+}
+
+InputDispatcher::InputDispatcher(Task* task, UINT message_waiting_for)
+ : task_(task), message_waiting_for_(message_waiting_for) {
+ InstallHook(this, message_waiting_for == WM_KEYUP);
+}
+
+InputDispatcher::~InputDispatcher() {
+ // Make sure the hook isn't installed.
+ UninstallHook(this);
+}
+
+void InputDispatcher::DispatchedMessage(WPARAM message) {
+ if (message == message_waiting_for_)
+ MatchingMessageFound();
+}
+
+void InputDispatcher::MatchingMessageFound() {
+ UninstallHook(this);
+ // At the time we're invoked the event has not actually been processed.
+ // Use PostTask to make sure the event has been processed before notifying.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, NewRunnableMethod(this, &InputDispatcher::NotifyTask), 0);
+}
+
+void InputDispatcher::NotifyTask() {
+ task_->Run();
+ Release();
+}
+
+// Private functions ----------------------------------------------------------
// Populate the INPUT structure with the appropriate keyboard event
// parameters required by SendInput
@@ -58,9 +181,11 @@ bool SendKeyEvent(wchar_t key, bool up) {
return true;
}
-namespace ui_controls {
+bool SendKeyPressImpl(wchar_t key, bool control, bool shift, bool alt,
+ Task* task) {
+ scoped_refptr<InputDispatcher> dispatcher(
+ task ? new InputDispatcher(task, WM_KEYUP) : NULL);
-bool SendKeyPress(wchar_t key, bool control, bool shift, bool alt) {
INPUT input[8] = { 0 }; // 8, assuming all the modifiers are activated
int i = 0;
@@ -108,21 +233,17 @@ bool SendKeyPress(wchar_t key, bool control, bool shift, bool alt) {
i++;
}
-
unsigned int rv = ::SendInput(i, input, sizeof(INPUT));
- return rv == i;
-}
-
-bool SendKeyDown(wchar_t key) {
- return SendKeyEvent(key, false);
-}
+ if (rv != i)
+ return false;
-bool SendKeyUp(wchar_t key) {
- return SendKeyEvent(key, true);
+ if (dispatcher.get())
+ dispatcher->AddRef();
+ return true;
}
-bool SendMouseMove(long x, long y) {
+bool SendMouseMoveImpl(long x, long y, Task* task) {
INPUT input = { 0 };
int screen_width = ::GetSystemMetrics(SM_CXSCREEN) - 1;
@@ -135,48 +256,115 @@ bool SendMouseMove(long x, long y) {
input.mi.dx = pixel_x;
input.mi.dy = pixel_y;
+ scoped_refptr<InputDispatcher> dispatcher(
+ task ? new InputDispatcher(task, WM_MOUSEMOVE) : NULL);
+
if (!::SendInput(1, &input, sizeof(INPUT)))
return false;
+
+ if (dispatcher.get())
+ dispatcher->AddRef();
+
return true;
}
-bool SendMouseClick(MouseButton type) {
-
+bool SendMouseEventsImpl(MouseButton type, int state, Task* task) {
DWORD down_flags = MOUSEEVENTF_ABSOLUTE;
DWORD up_flags = MOUSEEVENTF_ABSOLUTE;
+ UINT last_event;
switch(type) {
case LEFT:
down_flags |= MOUSEEVENTF_LEFTDOWN;
up_flags |= MOUSEEVENTF_LEFTUP;
-
+ last_event = (state & UP) ? WM_LBUTTONUP : WM_LBUTTONDOWN;
break;
+
case MIDDLE:
down_flags |= MOUSEEVENTF_MIDDLEDOWN;
up_flags |= MOUSEEVENTF_MIDDLEUP;
-
+ last_event = (state & UP) ? WM_MBUTTONUP : WM_MBUTTONDOWN;
break;
+
case RIGHT:
down_flags |= MOUSEEVENTF_RIGHTDOWN;
up_flags |= MOUSEEVENTF_RIGHTUP;
-
+ last_event = (state & UP) ? WM_RBUTTONUP : WM_RBUTTONDOWN;
break;
+
default:
NOTREACHED();
return false;
}
+ scoped_refptr<InputDispatcher> dispatcher(
+ task ? new InputDispatcher(task, last_event) : NULL);
+
INPUT input = { 0 };
input.type = INPUT_MOUSE;
input.mi.dwFlags = down_flags;
- if (!::SendInput(1, &input, sizeof(INPUT)))
+ if ((state & DOWN) && !::SendInput(1, &input, sizeof(INPUT)))
return false;
input.mi.dwFlags = up_flags;
- if (!::SendInput(1, &input, sizeof(INPUT)))
+ if ((state & UP) && !::SendInput(1, &input, sizeof(INPUT)))
return false;
+ if (dispatcher.get())
+ dispatcher->AddRef();
+
return true;
}
+} // namespace
+
+// public functions -----------------------------------------------------------
+
+bool SendKeyPress(wchar_t key, bool control, bool shift, bool alt) {
+ return SendKeyPressImpl(key, control, shift, alt, NULL);
+}
+
+bool SendKeyPressNotifyWhenDone(wchar_t key, bool control, bool shift,
+ bool alt, Task* task) {
+ return SendKeyPressImpl(key, control, shift, alt, task);
+}
+
+bool SendKeyDown(wchar_t key) {
+ return SendKeyEvent(key, false);
+}
+
+bool SendKeyUp(wchar_t key) {
+ return SendKeyEvent(key, true);
+}
+
+bool SendMouseMove(long x, long y) {
+ return SendMouseMoveImpl(x, y, NULL);
+}
+
+void SendMouseMoveNotifyWhenDone(long x, long y, Task* task) {
+ SendMouseMoveImpl(x, y, task);
+}
+
+bool SendMouseEvents(MouseButton type, int state) {
+ return SendMouseEventsImpl(type, state, NULL);
+}
+
+void SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task) {
+ SendMouseEventsImpl(type, state, task);
+}
+
+bool SendMouseClick(MouseButton type) {
+ return SendMouseEventsImpl(type, UP | DOWN, NULL);
+}
+
+void MoveMouseToCenterAndPress(
+ ChromeViews::View* view, MouseButton button, int state, Task* task) {
+ DCHECK(view);
+ DCHECK(view->GetViewContainer());
+ CPoint view_center(view->GetWidth() / 2, view->GetHeight() / 2);
+ ChromeViews::View::ConvertPointToScreen(view, &view_center);
+ SendMouseMove(view_center.x, view_center.y);
+ SendMouseEventsNotifyWhenDone(button, state, task);
+}
+
} // ui_controls
diff --git a/chrome/browser/automation/ui_controls.h b/chrome/browser/automation/ui_controls.h
index 4a655f5..35c0540 100644
--- a/chrome/browser/automation/ui_controls.h
+++ b/chrome/browser/automation/ui_controls.h
@@ -33,10 +33,22 @@
#include <string>
#include <wtypes.h>
+namespace ChromeViews {
+class View;
+}
+
+class Task;
+
namespace ui_controls {
+// Many of the functions in this class include a variant that takes a Task.
+// The version that takes a Task waits until the generated event is processed.
+// Once the generated event is processed the Task is Run (and deleted).
+
// Send a key press with/without modifier keys.
bool SendKeyPress(wchar_t key, bool control, bool shift, bool alt);
+bool SendKeyPressNotifyWhenDone(wchar_t key, bool control, bool shift,
+ bool alt, Task* task);
// Send a key down event. Use VK_CONTROL for ctrl key,
// VK_MENU for alt key and VK_SHIFT for shift key.
@@ -47,6 +59,7 @@ bool SendKeyUp(wchar_t key);
// Simulate a mouse move. (x,y) are absolute
// screen coordinates.
bool SendMouseMove(long x, long y);
+void SendMouseMoveNotifyWhenDone(long x, long y, Task* task);
enum MouseButton {
LEFT = 0,
@@ -54,9 +67,24 @@ enum MouseButton {
RIGHT,
};
+// Used to indicate the state of the button when generating events.
+enum MouseButtonState {
+ UP = 1,
+ DOWN = 2
+};
+
+// Sends a mouse down and or up message.
+bool SendMouseEvents(MouseButton type, int state);
+void SendMouseEventsNotifyWhenDone(MouseButton type, int state, Task* task);
+
// Simulate a single mouse click with given button type.
bool SendMouseClick(MouseButton type);
+// A combination of SendMouseMove to the middle of the view followed by
+// SendMouseEvents.
+void MoveMouseToCenterAndPress(
+ ChromeViews::View* view, MouseButton button, int state, Task* task);
+
} // ui_controls
#endif // CHROME_BROWSER_AUTOMATION_UI_CONTROLS_H__ \ No newline at end of file
diff --git a/chrome/browser/bookmark_bar_model.cc b/chrome/browser/bookmark_bar_model.cc
index 661f7c5..c6c9312 100644
--- a/chrome/browser/bookmark_bar_model.cc
+++ b/chrome/browser/bookmark_bar_model.cc
@@ -107,8 +107,13 @@ BookmarkBarModel::BookmarkBarModel(Profile* profile)
next_group_id_(HistoryService::kBookmarkBarID + 1),
bookmark_bar_node_(NULL),
other_node_(NULL) {
- if (!profile) {
- // Profile is NULL during testing.
+ // Notifications we want.
+ if (profile_)
+ NotificationService::current()->AddObserver(
+ this, NOTIFY_STARRED_FAVICON_CHANGED, Source<Profile>(profile_));
+
+ if (!profile || !profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)) {
+ // Profile/HistoryService is NULL during testing.
CreateBookmarkBarNode();
CreateOtherBookmarksNode();
AddRootChildren(NULL);
@@ -116,19 +121,11 @@ BookmarkBarModel::BookmarkBarModel(Profile* profile)
return;
}
- // Notifications we want.
- NotificationService::current()->AddObserver(
- this, NOTIFY_STARRED_FAVICON_CHANGED, Source<Profile>(profile_));
-
- // Request the entries from the database.
- HistoryService* history_service =
- profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
- if (!history_service)
- return;
-
// Request the entries on the bookmark bar.
- history_service->GetAllStarredEntries(&load_consumer_,
- NewCallback(this, &BookmarkBarModel::OnGotStarredEntries));
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)->
+ GetAllStarredEntries(&load_consumer_,
+ NewCallback(this,
+ &BookmarkBarModel::OnGotStarredEntries));
}
BookmarkBarModel::~BookmarkBarModel() {
diff --git a/chrome/browser/bookmark_bar_model.h b/chrome/browser/bookmark_bar_model.h
index b7e75f8..f7ecf71 100644
--- a/chrome/browser/bookmark_bar_model.h
+++ b/chrome/browser/bookmark_bar_model.h
@@ -27,8 +27,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#ifndef CHROME_BROWSER_BOOKMARK_BAR_H__
-#define CHROME_BROWSER_BOOKMARK_BAR_H__
+#ifndef CHROME_BROWSER_BOOKMARK_BAR_MODEL_H_
+#define CHROME_BROWSER_BOOKMARK_BAR_MODEL_H_
#include "base/observer_list.h"
#include "chrome/browser/cancelable_request.h"
@@ -427,4 +427,4 @@ class BookmarkBarModel : public NotificationObserver {
DISALLOW_EVIL_CONSTRUCTORS(BookmarkBarModel);
};
-#endif // CHROME_BROWSER_BOOKMARK_BAR_H__
+#endif // CHROME_BROWSER_BOOKMARK_BAR_MODEL_H_
diff --git a/chrome/browser/views/bookmark_bar_view.cc b/chrome/browser/views/bookmark_bar_view.cc
index e9dbb11..6579a0f 100644
--- a/chrome/browser/views/bookmark_bar_view.cc
+++ b/chrome/browser/views/bookmark_bar_view.cc
@@ -235,7 +235,13 @@ class BookmarkButton : public ChromeViews::TextButton {
url_(url),
profile_(profile) {
show_animation_.reset(new SlideAnimation(this));
- show_animation_->Show();
+ if (BookmarkBarView::testing_) {
+ // For some reason during testing the events generated by animating
+ // throw off the test. So, don't animate while testing.
+ show_animation_->Reset(1);
+ } else {
+ show_animation_->Show();
+ }
}
bool GetTooltipText(int x, int y, std::wstring* tooltip) {
@@ -507,6 +513,8 @@ class BookmarkNodeMenuController : public ChromeViews::MenuDelegate,
menu_.Cancel();
}
+ MenuItemView* menu() { return &menu_; }
+
private:
// Menu::Delegate method. Does the appropriate operation based on chosen
// menu item.
@@ -750,6 +758,12 @@ class MenuRunner : public ChromeViews::MenuDelegate,
menu_.Cancel();
}
+ MenuItemView* menu() { return &menu_; }
+
+ MenuItemView* context_menu() {
+ return context_menu_.get() ? context_menu_->menu() : NULL;
+ }
+
private:
// Creates an entry in menu for each child node of parent starting at
// start_child_index, recursively invoking this for any star groups.
@@ -980,6 +994,9 @@ class ButtonSeparatorView : public ChromeViews::View {
// static
const int BookmarkBarView::kMaxButtonWidth = 150;
+// static
+bool BookmarkBarView::testing_ = false;
+
// Returns the bitmap to use for starred groups.
static const SkBitmap& GetGroupIcon() {
if (!kFolderIcon) {
@@ -1444,24 +1461,38 @@ bool BookmarkBarView::IsAlwaysShown() {
}
bool BookmarkBarView::IsNewTabPage() {
- DCHECK(browser_);
-
- if (browser_->GetSelectedTabContents()) {
- return browser_->GetSelectedTabContents()->IsBookmarkBarAlwaysVisible();
- }
-
- return false;
+ return (browser_ && browser_->GetSelectedTabContents() &&
+ browser_->GetSelectedTabContents()->IsBookmarkBarAlwaysVisible());
}
void BookmarkBarView::AnimationProgressed(const Animation* animation) {
- browser_->ToolbarSizeChanged(NULL, true);
+ if (browser_)
+ browser_->ToolbarSizeChanged(NULL, true);
}
void BookmarkBarView::AnimationEnded(const Animation* animation) {
- browser_->ToolbarSizeChanged(NULL, false);
+ if (browser_)
+ browser_->ToolbarSizeChanged(NULL, false);
SchedulePaint();
}
+ChromeViews::TextButton* BookmarkBarView::GetBookmarkButton(int index) {
+ DCHECK(index >= 0 && index < GetBookmarkButtonCount());
+ return static_cast<ChromeViews::TextButton*>(GetChildViewAt(index));
+}
+
+ChromeViews::MenuItemView* BookmarkBarView::GetMenu() {
+ return menu_runner_.get() ? menu_runner_->menu() : NULL;
+}
+
+ChromeViews::MenuItemView* BookmarkBarView::GetContextMenu() {
+ return menu_runner_.get() ? menu_runner_->context_menu() : NULL;
+}
+
+ChromeViews::MenuItemView* BookmarkBarView::GetDropMenu() {
+ return drop_menu_runner_.get() ? drop_menu_runner_->menu() : NULL;
+}
+
void BookmarkBarView::Init() {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
@@ -1525,11 +1556,6 @@ int BookmarkBarView::GetBookmarkButtonCount() {
return GetChildViewCount() - 4;
}
-ChromeViews::TextButton* BookmarkBarView::GetBookmarkButton(int index) {
- DCHECK(index >= 0 && index < GetBookmarkButtonCount());
- return static_cast<ChromeViews::TextButton*>(GetChildViewAt(index));
-}
-
void BookmarkBarView::Loaded(BookmarkBarModel* model) {
BookmarkBarNode* node = model_->GetBookmarkBarNode();
DCHECK(node && model_->other_node());
@@ -1722,8 +1748,7 @@ void BookmarkBarView::RunMenu(ChromeViews::View* view,
CPoint screen_loc(x, 0);
View::ConvertPointToScreen(this, &screen_loc);
menu_runner_.reset(new MenuRunner(this, node, start_index));
- HWND parent_hwnd = reinterpret_cast<HWND>(
- browser_->window()->GetPlatformID());
+ HWND parent_hwnd = GetViewContainer()->GetHWND();
menu_runner_->RunMenuAt(parent_hwnd,
gfx::Rect(screen_loc.x, screen_loc.y,
view->GetWidth(), height),
@@ -1914,6 +1939,12 @@ void BookmarkBarView::StopShowFolderDropMenuTimer() {
}
void BookmarkBarView::StartShowFolderDropMenuTimer(BookmarkBarNode* node) {
+ if (testing_) {
+ // So that tests can run as fast as possible disable the delay during
+ // testing.
+ ShowDropFolderForNode(node);
+ return;
+ }
DCHECK(!show_folder_drop_menu_task_);
show_folder_drop_menu_task_ = new ShowFolderDropMenuTask(this, node);
static DWORD delay = 0;
diff --git a/chrome/browser/views/bookmark_bar_view.h b/chrome/browser/views/bookmark_bar_view.h
index b8ffa37..287f8d7 100644
--- a/chrome/browser/views/bookmark_bar_view.h
+++ b/chrome/browser/views/bookmark_bar_view.h
@@ -52,6 +52,10 @@ class ButtonSeparatorView;
struct DropInfo;
}
+namespace ChromeViews {
+class MenuItemView;
+}
+
// BookmarkBarView renders the BookmarkBarModel. Each starred entry
// on the BookmarkBar is rendered as a MenuButton. An additional
// MenuButton aligned to the right allows the user to quickly see
@@ -143,9 +147,33 @@ class BookmarkBarView : public ChromeViews::View,
void AnimationProgressed(const Animation* animation);
void AnimationEnded(const Animation* animation);
+ // Returns the button at the specified index.
+ ChromeViews::TextButton* GetBookmarkButton(int index);
+
+ // Returns the button responsible for showing bookmarks in the other bookmark
+ // folder.
+ ChromeViews::TextButton* other_bookmarked_button() const {
+ return other_bookmarked_button_;
+ }
+
+ // Returns the active MenuItemView, or NULL if a menu isn't showing.
+ ChromeViews::MenuItemView* GetMenu();
+
+ // Returns the drop MenuItemView, or NULL if a menu isn't showing.
+ ChromeViews::MenuItemView* GetDropMenu();
+
+ // Returns the context menu, or null if one isn't showing.
+ ChromeViews::MenuItemView* GetContextMenu();
+
+ // Returns the button used when not all the items on the bookmark bar fit.
+ ChromeViews::TextButton* overflow_button() const { return overflow_button_; }
+
// Maximum size of buttons on the bookmark bar.
static const int kMaxButtonWidth;
+ // If true we're running tests. This short circuits a couple of animations.
+ static bool testing_;
+
private:
// Task that invokes ShowDropFolderForNode when run. ShowFolderDropMenuTask
// deletes itself once run.
@@ -192,9 +220,6 @@ class BookmarkBarView : public ChromeViews::View,
// bookmark bar model has.
int GetBookmarkButtonCount();
- // Returns the button at the specified index.
- ChromeViews::TextButton* GetBookmarkButton(int index);
-
// Invoked when the bookmark bar model has finished loading. Creates a button
// for each of the children of the root node from the model.
virtual void Loaded(BookmarkBarModel* model);
@@ -404,7 +429,7 @@ class BookmarkBarView : public ChromeViews::View,
ButtonSeparatorView* bookmarks_separator_view_;
- // Owning browser.
+ // Owning browser. This is NULL duing testing.
Browser* browser_;
// Animation controlling showing and hiding of the bar.
diff --git a/chrome/browser/views/bookmark_bar_view_test.cc b/chrome/browser/views/bookmark_bar_view_test.cc
new file mode 100644
index 0000000..a2a84e7
--- /dev/null
+++ b/chrome/browser/views/bookmark_bar_view_test.cc
@@ -0,0 +1,857 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/string_util.h"
+#include "chrome/browser/automation/ui_controls.h"
+#include "chrome/browser/bookmark_bar_model.h"
+#include "chrome/browser/page_navigator.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/views/bookmark_bar_view.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/test/testing_profile.h"
+#include "chrome/test/ui/view_event_test_base.h"
+#include "chrome/views/chrome_menu.h"
+#include "chrome/views/text_button.h"
+
+namespace {
+
+// PageNavigator implementation that records the URL.
+class TestingPageNavigator : public PageNavigator {
+ public:
+ virtual void OpenURL(const GURL& url,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {
+ url_ = url;
+ }
+
+ GURL url_;
+};
+
+} // namespace
+
+// Base class for event generating bookmark view tests. These test are intended
+// to exercise ChromeMenus, but that's easier done with BookmarkBarView rather
+// than ChromeMenu itself.
+//
+// SetUp creates a bookmark model with the following structure.
+// All folders are in upper case, all URLs in lower case.
+// F1
+// f1a
+// F11
+// f11a
+// *
+// a
+// b
+// c
+// d
+// OTHER
+// oa
+// OF
+// ofa
+// ofb
+// OF2
+// of2a
+// of2b
+//
+// * if CreateBigMenu returns return true, 100 menu items are created here with
+// the names f1-f100.
+//
+// Subclasses should be sure and invoke super's implementation of SetUp and
+// TearDown.
+class BookmarkBarViewEventTestBase : public ViewEventTestBase {
+ public:
+ BookmarkBarViewEventTestBase()
+ : ViewEventTestBase(), bb_view_(NULL), model_(NULL) {
+ }
+
+ virtual void SetUp() {
+ ChromeViews::MenuItemView::allow_task_nesting_during_run_ = true;
+ BookmarkBarView::testing_ = true;
+
+ profile_.reset(new TestingProfile());
+ profile_->set_has_history_service(true);
+ profile_->CreateBookmarkBarModel();
+ profile_->GetPrefs()->SetBoolean(prefs::kShowBookmarkBar, true);
+
+ model_ = profile_->GetBookmarkBarModel();
+
+ bb_view_ = new BookmarkBarView(profile_.get(), NULL);
+ bb_view_->SetPageNavigator(&navigator_);
+
+ AddTestData(CreateBigMenu());
+
+ // Calculate the preferred size so that one button doesn't fit, which
+ // triggers the overflow button to appear.
+ //
+ // BookmarkBarView::Layout does nothing if the parent is NULL and
+ // GetPreferredSize hard codes a width of 1. For that reason we add the
+ // BookmarkBarView to a dumby view as the parent.
+ //
+ // This code looks a bit hacky, but I've written it so that it shouldn't
+ // be dependant upon any of the layout code in BookmarkBarView. Instead
+ // we brute force search for a size that triggers the overflow button.
+ ChromeViews::View tmp_parent;
+
+ tmp_parent.AddChildView(bb_view_);
+
+ CSize bb_view_pref;
+ bb_view_->GetPreferredSize(&bb_view_pref);
+ bb_view_pref_.set_width(1000);
+ bb_view_pref_.set_height(bb_view_pref.cy);
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(4);
+ while (button->IsVisible()) {
+ bb_view_pref_.set_width(bb_view_pref_.width() - 25);
+ bb_view_->SetBounds(0, 0, bb_view_pref_.width(), bb_view_pref_.height());
+ bb_view_->Layout();
+ }
+
+ tmp_parent.RemoveChildView(bb_view_);
+
+ ViewEventTestBase::SetUp();
+ }
+
+ virtual void TearDown() {
+ BookmarkBarView::testing_ = false;
+ ChromeViews::MenuItemView::allow_task_nesting_during_run_ = false;
+ ViewEventTestBase::TearDown();
+ }
+
+ protected:
+ virtual ChromeViews::View* CreateContentsView() {
+ return bb_view_;
+ }
+
+ virtual gfx::Size GetPreferredSize() { return bb_view_pref_; }
+
+ // See comment above class description for what this does.
+ virtual bool CreateBigMenu() { return false; }
+
+ BookmarkBarModel* model_;
+ BookmarkBarView* bb_view_;
+ TestingPageNavigator navigator_;
+
+ private:
+ void AddTestData(bool big_menu) {
+ std::string test_base = "file:///c:/tmp/";
+
+ BookmarkBarNode* f1 =
+ model_->AddGroup(model_->GetBookmarkBarNode(), 0, L"F1");
+ model_->AddURL(f1, 0, L"f1a", GURL(test_base + "f1a"));
+ BookmarkBarNode* f11 = model_->AddGroup(f1, 1, L"F11");
+ model_->AddURL(f11, 0, L"f11a", GURL(test_base + "f11a"));
+ if (big_menu) {
+ for (int i = 1; i <= 100; ++i) {
+ model_->AddURL(f1, i + 1, L"f" + IntToWString(i),
+ GURL(test_base + "f" + IntToString(i)));
+ }
+ }
+ model_->AddURL(model_->GetBookmarkBarNode(), 1, L"a",
+ GURL(test_base + "a"));
+ model_->AddURL(model_->GetBookmarkBarNode(), 2, L"b",
+ GURL(test_base + "b"));
+ model_->AddURL(model_->GetBookmarkBarNode(), 3, L"c",
+ GURL(test_base + "c"));
+ model_->AddURL(model_->GetBookmarkBarNode(), 4, L"d",
+ GURL(test_base + "d"));
+ model_->AddURL(model_->other_node(), 0, L"oa", GURL(test_base + "oa"));
+ BookmarkBarNode* of = model_->AddGroup(model_->other_node(), 1, L"OF");
+ model_->AddURL(of, 0, L"ofa", GURL(test_base + "ofa"));
+ model_->AddURL(of, 1, L"ofb", GURL(test_base + "ofb"));
+ BookmarkBarNode* of2 = model_->AddGroup(model_->other_node(), 2, L"OF2");
+ model_->AddURL(of2, 0, L"of2a", GURL(test_base + "of2a"));
+ model_->AddURL(of2, 1, L"of2b", GURL(test_base + "of2b"));
+ }
+
+ gfx::Size bb_view_pref_;
+ scoped_ptr<TestingProfile> profile_;
+};
+
+// Clicks on first menu, makes sure button is depressed. Moves mouse to first
+// child, clicks it and makes sure a navigation occurs.
+class BookmarkBarViewTest1 : public BookmarkBarViewEventTestBase {
+ protected:
+ virtual void DoTestOnMessageLoop() {
+ // Move the mouse to the first folder on the bookmark bar and press the
+ // mouse.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest1::Step2));
+ }
+
+ private:
+ void Step2() {
+ // Menu should be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+
+ // Button should be depressed.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ ASSERT_TRUE(button->GetState() == ChromeViews::BaseButton::BS_PUSHED);
+
+ // Click on the 2nd menu item (A URL).
+ ASSERT_TRUE(menu->GetSubmenu());
+
+ ChromeViews::MenuItemView* menu_to_select =
+ menu->GetSubmenu()->GetMenuItemAt(0);
+ ui_controls::MoveMouseToCenterAndPress(menu_to_select, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest1::Step3));
+ }
+
+ void Step3() {
+ // We should have navigated to URL f1a.
+ ASSERT_TRUE(navigator_.url_ ==
+ model_->GetBookmarkBarNode()->GetChild(0)->GetChild(0)->
+ GetURL());
+
+ // Make sure button is no longer pushed.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ ASSERT_TRUE(button->GetState() == ChromeViews::BaseButton::BS_NORMAL);
+
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu == NULL || !menu->GetSubmenu()->IsShowing());
+
+ Done();
+ }
+};
+
+VIEW_TEST(BookmarkBarViewTest1, Basic)
+
+// Brings up menu, clicks on empty space and make sure menu hides.
+class BookmarkBarViewTest2 : public BookmarkBarViewEventTestBase {
+ protected:
+ virtual void DoTestOnMessageLoop() {
+ // Move the mouse to the first folder on the bookmark bar and press the
+ // mouse.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest2::Step2));
+ }
+
+ private:
+ void Step2() {
+ // Menu should be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL && menu->GetSubmenu()->IsShowing());
+
+ // Click on 0x0, which should trigger closing menu.
+ // NOTE: this code assume there is a left margin, which is currently
+ // true. If that changes, this code will need to find another empty space
+ // to press the mouse on.
+ CPoint mouse_loc(0, 0);
+ ChromeViews::View::ConvertPointToScreen(bb_view_, &mouse_loc);
+ ui_controls::SendMouseMove(0, 0);
+ ui_controls::SendMouseEventsNotifyWhenDone(
+ ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest2::Step3));
+ }
+
+ void Step3() {
+ // The menu shouldn't be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu == NULL || !menu->GetSubmenu()->IsShowing());
+
+ // Make sure button is no longer pushed.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ ASSERT_TRUE(button->GetState() == ChromeViews::BaseButton::BS_NORMAL);
+
+ window_->Activate();
+
+ Done();
+ }
+};
+
+VIEW_TEST(BookmarkBarViewTest2, HideOnDesktopClick)
+
+// Brings up menu. Moves over child to make sure submenu appears, moves over
+// another child and make sure next menu appears.
+class BookmarkBarViewTest3 : public BookmarkBarViewEventTestBase {
+ protected:
+ virtual void DoTestOnMessageLoop() {
+ // Move the mouse to the first folder on the bookmark bar and press the
+ // mouse.
+ ChromeViews::TextButton* button = bb_view_->other_bookmarked_button();
+ ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest3::Step2));
+ }
+
+ private:
+ void Step2() {
+ // Menu should be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+
+ ChromeViews::MenuItemView* child_menu =
+ menu->GetSubmenu()->GetMenuItemAt(1);
+ ASSERT_TRUE(child_menu != NULL);
+
+ // Click on second child, which has a submenu.
+ ui_controls::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest3::Step3));
+ }
+
+ void Step3() {
+ // Make sure sub menu is showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ChromeViews::MenuItemView* child_menu =
+ menu->GetSubmenu()->GetMenuItemAt(1);
+ ASSERT_TRUE(child_menu->GetSubmenu() != NULL);
+ ASSERT_TRUE(child_menu->GetSubmenu()->IsShowing());
+
+ // Click on third child, which has a submenu too.
+ child_menu = menu->GetSubmenu()->GetMenuItemAt(2);
+ ASSERT_TRUE(child_menu != NULL);
+ ui_controls::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest3::Step4));
+ }
+
+ void Step4() {
+ // Make sure sub menu we first clicked isn't showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ChromeViews::MenuItemView* child_menu =
+ menu->GetSubmenu()->GetMenuItemAt(1);
+ ASSERT_TRUE(child_menu->GetSubmenu() != NULL);
+ ASSERT_FALSE(child_menu->GetSubmenu()->IsShowing());
+
+ // And submenu we last clicked is showing.
+ child_menu = menu->GetSubmenu()->GetMenuItemAt(2);
+ ASSERT_TRUE(child_menu != NULL);
+ ASSERT_TRUE(child_menu->GetSubmenu()->IsShowing());
+
+ // Nothing should have been selected.
+ ASSERT_TRUE(navigator_.url_ == GURL());
+
+ // Hide menu.
+ menu->GetMenuController()->Cancel(true);
+
+ // Because of the nested loop run by the menu we need to invoke done twice.
+ Done();
+ Done();
+ }
+};
+
+VIEW_TEST(BookmarkBarViewTest3, Submenus)
+
+// Tests context menus by way of opening a context menu for a bookmark,
+// then right clicking to get context menu and selecting the first menu item
+// (open).
+class BookmarkBarViewTest4 : public BookmarkBarViewEventTestBase {
+ protected:
+ virtual void DoTestOnMessageLoop() {
+ // Move the mouse to the first folder on the bookmark bar and press the
+ // mouse.
+ ChromeViews::TextButton* button = bb_view_->other_bookmarked_button();
+ ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest4::Step2));
+ }
+
+ private:
+ void Step2() {
+ // Menu should be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+
+ ChromeViews::MenuItemView* child_menu =
+ menu->GetSubmenu()->GetMenuItemAt(0);
+ ASSERT_TRUE(child_menu != NULL);
+
+ // Right click on the first child to get its context menu.
+ ui_controls::MoveMouseToCenterAndPress(child_menu, ui_controls::RIGHT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest4::Step3));
+ }
+
+ void Step3() {
+ // Make sure the context menu is showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetContextMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu());
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+
+ // Select the first menu item (open).
+ ui_controls::MoveMouseToCenterAndPress(menu->GetSubmenu()->GetMenuItemAt(0),
+ ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest4::Step4));
+ }
+
+ void Step4() {
+ ASSERT_TRUE(navigator_.url_ ==
+ model_->other_node()->GetChild(0)->GetURL());
+
+ // Because of the nested loop we invoke done twice here.
+ Done();
+ Done();
+ }
+};
+
+VIEW_TEST(BookmarkBarViewTest4, ContextMenus)
+
+// Tests drag and drop within the same menu.
+class BookmarkBarViewTest5 : public BookmarkBarViewEventTestBase {
+ protected:
+ virtual void DoTestOnMessageLoop() {
+ url_dragging_ =
+ model_->GetBookmarkBarNode()->GetChild(0)->GetChild(0)->GetURL();
+
+ // Move the mouse to the first folder on the bookmark bar and press the
+ // mouse.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest5::Step2));
+ }
+
+ private:
+ void Step2() {
+ // Menu should be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+
+ ChromeViews::MenuItemView* child_menu =
+ menu->GetSubmenu()->GetMenuItemAt(0);
+ ASSERT_TRUE(child_menu != NULL);
+
+ // Move mouse to center of menu and press button.
+ ui_controls::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
+ ui_controls::DOWN,
+ CreateEventTask(this, &BookmarkBarViewTest5::Step3));
+ }
+
+ void Step3() {
+ ChromeViews::MenuItemView* target_menu =
+ bb_view_->GetMenu()->GetSubmenu()->GetMenuItemAt(1);
+ CPoint loc(1, target_menu->GetHeight() - 1);
+ ChromeViews::View::ConvertPointToScreen(target_menu, &loc);
+
+ // Start a drag.
+ ui_controls::SendMouseMoveNotifyWhenDone(loc.x + 10, loc.y,
+ CreateEventTask(this, &BookmarkBarViewTest5::Step4));
+
+ // See comment above this method as to why we do this.
+ ScheduleMouseMoveInBackground(loc.x, loc.y);
+ }
+
+ void Step4() {
+ // Drop the item so that it's now the second item.
+ ChromeViews::MenuItemView* target_menu =
+ bb_view_->GetMenu()->GetSubmenu()->GetMenuItemAt(1);
+ CPoint loc(1, target_menu->GetHeight() - 1);
+ ChromeViews::View::ConvertPointToScreen(target_menu, &loc);
+ ui_controls::SendMouseMove(loc.x, loc.y);
+
+ ui_controls::SendMouseEventsNotifyWhenDone(ui_controls::LEFT,
+ ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest5::Step5));
+ }
+
+ void Step5() {
+ GURL url = model_->GetBookmarkBarNode()->GetChild(0)->GetChild(1)->GetURL();
+ ASSERT_TRUE(url == url_dragging_);
+ Done();
+ }
+
+ GURL url_dragging_;
+};
+
+VIEW_TEST(BookmarkBarViewTest5, DND)
+
+// Tests holding mouse down on overflow button, dragging such that menu pops up
+// then selecting an item.
+class BookmarkBarViewTest6 : public BookmarkBarViewEventTestBase {
+ protected:
+ virtual void DoTestOnMessageLoop() {
+ // Press the mouse button on the overflow button. Don't release it though.
+ ChromeViews::TextButton* button = bb_view_->overflow_button();
+ ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
+ ui_controls::DOWN, CreateEventTask(this, &BookmarkBarViewTest6::Step2));
+ }
+
+ private:
+ void Step2() {
+ // Menu should be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+
+ ChromeViews::MenuItemView* child_menu =
+ menu->GetSubmenu()->GetMenuItemAt(0);
+ ASSERT_TRUE(child_menu != NULL);
+
+ // Move mouse to center of menu and release mouse.
+ ui_controls::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
+ ui_controls::UP, CreateEventTask(this, &BookmarkBarViewTest6::Step3));
+ }
+
+ void Step3() {
+ ASSERT_TRUE(navigator_.url_ ==
+ model_->GetBookmarkBarNode()->GetChild(4)->GetURL());
+ Done();
+ }
+
+ GURL url_dragging_;
+};
+
+VIEW_TEST(BookmarkBarViewTest6, OpenMenuOnClickAndHold)
+
+// Tests drag and drop to different menu.
+class BookmarkBarViewTest7 : public BookmarkBarViewEventTestBase {
+ protected:
+ virtual void DoTestOnMessageLoop() {
+ url_dragging_ =
+ model_->GetBookmarkBarNode()->GetChild(0)->GetChild(0)->GetURL();
+
+ // Move the mouse to the first folder on the bookmark bar and press the
+ // mouse.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest7::Step2));
+ }
+
+ private:
+ void Step2() {
+ // Menu should be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+
+ ChromeViews::MenuItemView* child_menu =
+ menu->GetSubmenu()->GetMenuItemAt(0);
+ ASSERT_TRUE(child_menu != NULL);
+
+ // Move mouse to center of menu and press button.
+ ui_controls::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
+ ui_controls::DOWN,
+ CreateEventTask(this, &BookmarkBarViewTest7::Step3));
+ }
+
+ void Step3() {
+ // Drag over other button.
+ ChromeViews::TextButton* other_button =
+ bb_view_->other_bookmarked_button();
+ CPoint loc(other_button->GetWidth() / 2, other_button->GetHeight() / 2);
+ ChromeViews::View::ConvertPointToScreen(other_button, &loc);
+
+ // Start a drag.
+ ui_controls::SendMouseMoveNotifyWhenDone(loc.x + 10, loc.y,
+ NewRunnableMethod(this, &BookmarkBarViewTest7::Step4));
+
+ // See comment above this method as to why we do this.
+ ScheduleMouseMoveInBackground(loc.x, loc.y);
+ }
+
+ void Step4() {
+ ChromeViews::MenuItemView* drop_menu = bb_view_->GetDropMenu();
+ ASSERT_TRUE(drop_menu != NULL);
+ ASSERT_TRUE(drop_menu->GetSubmenu()->IsShowing());
+
+ ChromeViews::MenuItemView* target_menu =
+ drop_menu->GetSubmenu()->GetMenuItemAt(0);
+ CPoint loc(1, 1);
+ ChromeViews::View::ConvertPointToScreen(target_menu, &loc);
+ ui_controls::SendMouseMove(loc.x, loc.y);
+ ui_controls::SendMouseEventsNotifyWhenDone(
+ ui_controls::LEFT, ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest7::Step5));
+ }
+
+ void Step5() {
+ ASSERT_TRUE(model_->other_node()->GetChild(0)->GetURL() == url_dragging_);
+ Done();
+ }
+
+ GURL url_dragging_;
+};
+
+VIEW_TEST(BookmarkBarViewTest7, DNDToDifferentMenu)
+
+// Drags from one menu to next so that original menu closes, then back to
+// original menu.
+class BookmarkBarViewTest8 : public BookmarkBarViewEventTestBase {
+ protected:
+ virtual void DoTestOnMessageLoop() {
+ url_dragging_ =
+ model_->GetBookmarkBarNode()->GetChild(0)->GetChild(0)->GetURL();
+
+ // Move the mouse to the first folder on the bookmark bar and press the
+ // mouse.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest8::Step2));
+ }
+
+ private:
+ void Step2() {
+ // Menu should be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+
+ ChromeViews::MenuItemView* child_menu =
+ menu->GetSubmenu()->GetMenuItemAt(0);
+ ASSERT_TRUE(child_menu != NULL);
+
+ // Move mouse to center of menu and press button.
+ ui_controls::MoveMouseToCenterAndPress(child_menu, ui_controls::LEFT,
+ ui_controls::DOWN,
+ CreateEventTask(this, &BookmarkBarViewTest8::Step3));
+ }
+
+ void Step3() {
+ // Drag over other button.
+ ChromeViews::TextButton* other_button =
+ bb_view_->other_bookmarked_button();
+ CPoint loc(other_button->GetWidth() / 2, other_button->GetHeight() / 2);
+ ChromeViews::View::ConvertPointToScreen(other_button, &loc);
+
+ // Start a drag.
+ ui_controls::SendMouseMoveNotifyWhenDone(loc.x + 10, loc.y,
+ NewRunnableMethod(this, &BookmarkBarViewTest8::Step4));
+
+ // See comment above this method as to why we do this.
+ ScheduleMouseMoveInBackground(loc.x, loc.y);
+ }
+
+ void Step4() {
+ ChromeViews::MenuItemView* drop_menu = bb_view_->GetDropMenu();
+ ASSERT_TRUE(drop_menu != NULL);
+ ASSERT_TRUE(drop_menu->GetSubmenu()->IsShowing());
+
+ // Now drag back over first menu.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ CPoint loc(button->GetWidth() / 2, button->GetHeight() / 2);
+ ChromeViews::View::ConvertPointToScreen(button, &loc);
+ ui_controls::SendMouseMoveNotifyWhenDone(loc.x, loc.y,
+ NewRunnableMethod(this, &BookmarkBarViewTest8::Step5));
+ }
+
+ void Step5() {
+ // Drop on folder F11.
+ ChromeViews::MenuItemView* drop_menu = bb_view_->GetDropMenu();
+ ASSERT_TRUE(drop_menu != NULL);
+ ASSERT_TRUE(drop_menu->GetSubmenu()->IsShowing());
+
+ ChromeViews::MenuItemView* target_menu =
+ drop_menu->GetSubmenu()->GetMenuItemAt(1);
+ ui_controls::MoveMouseToCenterAndPress(
+ target_menu, ui_controls::LEFT, ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest8::Step6));
+ }
+
+ void Step6() {
+ // Make sure drop was processed.
+ GURL final_url = model_->GetBookmarkBarNode()->GetChild(0)->GetChild(0)->
+ GetChild(1)->GetURL();
+ ASSERT_TRUE(final_url == url_dragging_);
+ Done();
+ }
+
+ GURL url_dragging_;
+};
+
+VIEW_TEST(BookmarkBarViewTest8, DNDBackToOriginatingMenu)
+
+// Moves the mouse over the scroll button and makes sure we get scrolling.
+class BookmarkBarViewTest9 : public BookmarkBarViewEventTestBase {
+ protected:
+ virtual bool CreateBigMenu() { return true; }
+
+ virtual void DoTestOnMessageLoop() {
+ // Move the mouse to the first folder on the bookmark bar and press the
+ // mouse.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest9::Step2));
+ }
+
+ private:
+ void Step2() {
+ // Menu should be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+
+ first_menu_ = menu->GetSubmenu()->GetMenuItemAt(0);
+ CPoint menu_loc;
+ ChromeViews::View::ConvertPointToScreen(first_menu_, &menu_loc);
+ start_y_ = menu_loc.y;
+
+ // Move the mouse over the scroll button.
+ ChromeViews::View* scroll_container = menu->GetSubmenu()->GetParent();
+ ASSERT_TRUE(scroll_container != NULL);
+ scroll_container = scroll_container->GetParent();
+ ASSERT_TRUE(scroll_container != NULL);
+ ChromeViews::View* scroll_down_button = scroll_container->GetChildViewAt(1);
+ ASSERT_TRUE(scroll_down_button);
+ CPoint loc(scroll_down_button->GetWidth() / 2,
+ scroll_down_button->GetHeight() / 2);
+ ChromeViews::View::ConvertPointToScreen(scroll_down_button, &loc);
+ ui_controls::SendMouseMoveNotifyWhenDone(
+ loc.x, loc.y, CreateEventTask(this, &BookmarkBarViewTest9::Step3));
+ }
+
+ void Step3() {
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ NewRunnableMethod(this, &BookmarkBarViewTest9::Step4), 200);
+ }
+
+ void Step4() {
+ CPoint menu_loc;
+ ChromeViews::View::ConvertPointToScreen(first_menu_, &menu_loc);
+ ASSERT_NE(start_y_, menu_loc.y);
+
+ // Hide menu.
+ bb_view_->GetMenu()->GetMenuController()->Cancel(true);
+
+ Done();
+ }
+
+ int start_y_;
+ ChromeViews::MenuItemView* first_menu_;
+};
+
+VIEW_TEST(BookmarkBarViewTest9, ScrollButtonScrolls)
+
+// Tests up/down/left/enter key messages.
+class BookmarkBarViewTest10 : public BookmarkBarViewEventTestBase {
+ protected:
+ virtual void DoTestOnMessageLoop() {
+ // Move the mouse to the first folder on the bookmark bar and press the
+ // mouse.
+ ChromeViews::TextButton* button = bb_view_->GetBookmarkButton(0);
+ ui_controls::MoveMouseToCenterAndPress(button, ui_controls::LEFT,
+ ui_controls::DOWN | ui_controls::UP,
+ CreateEventTask(this, &BookmarkBarViewTest10::Step2));
+ }
+
+ private:
+ void Step2() {
+ // Menu should be showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+
+ // Send a down event, which should select the first item.
+ ui_controls::SendKeyPressNotifyWhenDone(
+ VK_DOWN, false, false, false,
+ CreateEventTask(this, &BookmarkBarViewTest10::Step3));
+ }
+
+ void Step3() {
+ // Make sure menu is showing and item is selected.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+ ASSERT_TRUE(menu->GetSubmenu()->GetMenuItemAt(0)->IsSelected());
+
+ // Send a key down event, which should select the next item.
+ ui_controls::SendKeyPressNotifyWhenDone(
+ VK_DOWN, false, false, false,
+ CreateEventTask(this, &BookmarkBarViewTest10::Step4));
+ }
+
+ void Step4() {
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+ ASSERT_FALSE(menu->GetSubmenu()->GetMenuItemAt(0)->IsSelected());
+ ASSERT_TRUE(menu->GetSubmenu()->GetMenuItemAt(1)->IsSelected());
+
+ // Send a right arrow to force the menu to open.
+ ui_controls::SendKeyPressNotifyWhenDone(
+ VK_RIGHT, false, false, false,
+ CreateEventTask(this, &BookmarkBarViewTest10::Step5));
+ }
+
+ void Step5() {
+ // Make sure the submenu is showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+ ChromeViews::MenuItemView* submenu = menu->GetSubmenu()->GetMenuItemAt(1);
+ ASSERT_TRUE(submenu->IsSelected());
+ ASSERT_TRUE(submenu->GetSubmenu());
+ ASSERT_TRUE(submenu->GetSubmenu()->IsShowing());
+
+ // Send a left arrow to close the submenu.
+ ui_controls::SendKeyPressNotifyWhenDone(
+ VK_LEFT, false, false, false,
+ CreateEventTask(this, &BookmarkBarViewTest10::Step6));
+ }
+
+ void Step6() {
+ // Make sure the submenu is showing.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+ ChromeViews::MenuItemView* submenu = menu->GetSubmenu()->GetMenuItemAt(1);
+ ASSERT_TRUE(submenu->IsSelected());
+ ASSERT_TRUE(!submenu->GetSubmenu() || !submenu->GetSubmenu()->IsShowing());
+
+ // Send a down arrow to wrap back to f1a
+ ui_controls::SendKeyPressNotifyWhenDone(
+ VK_DOWN, false, false, false,
+ CreateEventTask(this, &BookmarkBarViewTest10::Step7));
+ }
+
+ void Step7() {
+ // Make sure menu is showing and item is selected.
+ ChromeViews::MenuItemView* menu = bb_view_->GetMenu();
+ ASSERT_TRUE(menu != NULL);
+ ASSERT_TRUE(menu->GetSubmenu()->IsShowing());
+ ASSERT_TRUE(menu->GetSubmenu()->GetMenuItemAt(0)->IsSelected());
+
+ // Send enter, which should select the item.
+ ui_controls::SendKeyPressNotifyWhenDone(
+ VK_RETURN, false, false, false,
+ CreateEventTask(this, &BookmarkBarViewTest10::Step8));
+ }
+
+ void Step8() {
+ ASSERT_TRUE(
+ model_->GetBookmarkBarNode()->GetChild(0)->GetChild(0)->GetURL() ==
+ navigator_.url_);
+ Done();
+ }
+};
+
+VIEW_TEST(BookmarkBarViewTest10, KeyEvents)
diff --git a/chrome/test/testing_profile.cc b/chrome/test/testing_profile.cc
index ac9e9f7..c1c3fe8 100644
--- a/chrome/test/testing_profile.cc
+++ b/chrome/test/testing_profile.cc
@@ -67,3 +67,7 @@ void TestingProfile::DestroyHistoryService() {
MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask);
MessageLoop::current()->Run();
}
+
+void TestingProfile::CreateBookmarkBarModel() {
+ bookmark_bar_model_.reset(new BookmarkBarModel(this));
+}
diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h
index ae6c356..f6b01b5 100644
--- a/chrome/test/testing_profile.h
+++ b/chrome/test/testing_profile.h
@@ -33,6 +33,7 @@
#include "base/base_paths.h"
#include "base/path_service.h"
#include "base/file_util.h"
+#include "chrome/browser/bookmark_bar_model.h"
#include "chrome/browser/browser_prefs.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/profile.h"
@@ -40,12 +41,16 @@
class TestingProfile : public Profile {
public:
- TestingProfile() : start_time_(Time::Now()) {}
+ TestingProfile() : start_time_(Time::Now()), has_history_service_(false) {}
virtual ~TestingProfile();
// Creates the HistoryService. Normally there is no HistoryService.
void CreateHistoryService();
+ // Creates the BookmkarBarModel. If not invoked the bookmark bar model is
+ // NULL.
+ void CreateBookmarkBarModel();
+
virtual std::wstring GetPath() {
return std::wstring();
}
@@ -64,8 +69,11 @@ class TestingProfile : public Profile {
virtual HistoryService* GetHistoryService(ServiceAccessType access) {
return history_service_.get();
}
+ void set_has_history_service(bool has_history_service) {
+ has_history_service_ = has_history_service;
+ }
virtual bool HasHistoryService() const {
- return (history_service_.get() != NULL);
+ return (history_service_.get() != NULL || has_history_service_);
}
virtual WebDataService* GetWebDataService(ServiceAccessType access) {
return NULL;
@@ -134,10 +142,10 @@ class TestingProfile : public Profile {
virtual void MergeResourceBoolean(int message_id, bool* output_value) {
}
virtual bool HasBookmarkBarModel() {
- return false;
+ return (bookmark_bar_model_.get() != NULL);
}
virtual BookmarkBarModel* GetBookmarkBarModel() {
- return NULL;
+ return bookmark_bar_model_.get();
}
virtual bool Profile::IsSameProfile(Profile *p) {
return this == p;
@@ -171,6 +179,13 @@ class TestingProfile : public Profile {
// The history service. Only created if CreateHistoryService is invoked.
scoped_refptr<HistoryService> history_service_;
+
+ // The BookmarkBarModel. Only created if CreateBookmarkBarModel is invoked.
+ scoped_ptr<BookmarkBarModel> bookmark_bar_model_;
+
+ // Do we have a history service? This defaults to the value of
+ // history_service, but can be explicitly set.
+ bool has_history_service_;
};
#endif // CHROME_TEST_TESTING_PROFILE_H__
diff --git a/chrome/test/ui/view_event_test_base.cc b/chrome/test/ui/view_event_test_base.cc
new file mode 100644
index 0000000..bf0aa84
--- /dev/null
+++ b/chrome/test/ui/view_event_test_base.cc
@@ -0,0 +1,135 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/test/ui/view_event_test_base.h"
+
+#include "base/message_loop.h"
+#include "chrome/browser/automation/ui_controls.h"
+#include "chrome/views/window.h"
+
+namespace {
+
+// View subclass that allows you to specify the preferred size.
+class TestView : public ChromeViews::View {
+ public:
+ TestView() {}
+
+ void set_preferred_size(const gfx::Size& size) { preferred_size_ = size; }
+ void GetPreferredSize(CSize* out) {
+ if (!preferred_size_.IsEmpty())
+ *out = preferred_size_.ToSIZE();
+ else
+ View::GetPreferredSize(out);
+ }
+
+ private:
+ gfx::Size preferred_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestView);
+};
+
+// Delay in background thread before posting mouse move.
+const int kMouseMoveDelayMS = 200;
+
+} // namespace
+
+// static
+void ViewEventTestBase::Done() {
+ MessageLoop::current()->Quit();
+ MessageLoop::current()->Quit();
+}
+
+ViewEventTestBase::ViewEventTestBase() : window_(NULL), content_view_(NULL) { }
+
+void ViewEventTestBase::SetUp() {
+ OleInitialize(NULL);
+ window_ = ChromeViews::Window::CreateChromeWindow(NULL, gfx::Rect(), this);
+}
+
+void ViewEventTestBase::TearDown() {
+ if (window_) {
+ DestroyWindow(window_->GetHWND());
+ window_ = NULL;
+ }
+ OleUninitialize();
+}
+
+ChromeViews::View* ViewEventTestBase::GetContentsView() {
+ if (!content_view_) {
+ // Wrap the real view (as returned by CreateContentsView) in a View so
+ // that we can customize the preferred size.
+ TestView* test_view = new TestView();
+ test_view->SetLayoutManager(new ChromeViews::FillLayout());
+ test_view->set_preferred_size(GetPreferredSize());
+ test_view->AddChildView(CreateContentsView());
+ content_view_ = test_view;
+ }
+ return content_view_;
+}
+
+void ViewEventTestBase::StartMessageLoopAndRunTest() {
+ window_->Show();
+ // Make sure the window is the foreground window, otherwise none of the
+ // mouse events are going to be targeted correctly.
+ SetForegroundWindow(window_->GetHWND());
+
+ // Flush any pending events to make sure we start with a clean slate.
+ MessageLoop::current()->RunAllPending();
+
+ // Schedule a task that starts the test. Need to do this as we're going to
+ // run the message loop.
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ NewRunnableMethod(this, &ViewEventTestBase::DoTestOnMessageLoop), 0);
+
+ MessageLoop::current()->Run();
+}
+
+void ViewEventTestBase::ScheduleMouseMoveInBackground(int x, int y) {
+ if (!dnd_thread_.get()) {
+ dnd_thread_.reset(new Thread("mouse-move-thread"));
+ dnd_thread_->Start();
+ }
+ dnd_thread_->message_loop()->PostDelayedTask(
+ FROM_HERE, NewRunnableFunction(&ui_controls::SendMouseMove, x, y),
+ kMouseMoveDelayMS);
+}
+
+void ViewEventTestBase::StopBackgroundThread() {
+ dnd_thread_.reset(NULL);
+}
+
+void ViewEventTestBase::RunTestMethod(Task* task) {
+ StopBackgroundThread();
+
+ scoped_ptr<Task> task_deleter(task);
+ task->Run();
+ if (HasFatalFailure())
+ Done();
+}
diff --git a/chrome/test/ui/view_event_test_base.h b/chrome/test/ui/view_event_test_base.h
new file mode 100644
index 0000000..4582f8a
--- /dev/null
+++ b/chrome/test/ui/view_event_test_base.h
@@ -0,0 +1,160 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_TEST_UI_VIEW_EVENT_TEST_BASE_H_
+#define CHROME_TEST_UI_VIEW_EVENT_TEST_BASE_H_
+
+#include "base/thread.h"
+#include "chrome/views/window_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class Task;
+
+// Base class for Views based tests that dispatch events.
+//
+// As views based event test involves waiting for events to be processed,
+// writing a views based test is slightly different than that of writing
+// other unit tests. In particular when the test fails or is done you need
+// to stop the message loop. This can be done by way of invoking the Done
+// method.
+//
+// Any delayed callbacks should be done by way of CreateEventTask.
+// CreateEventTask checks to see if ASSERT_XXX has been invoked after invoking
+// the task. If there was a failure Done is invoked and the test stops.
+//
+// ViewEventTestBase creates a Window with the View returned from
+// CreateContentsView. The preferred size for the view can be customized by
+// overriding GetPreferredSize. If you do not override GetPreferredSize the
+// preferred size of the view returned from CreateContentsView is used.
+//
+// Subclasses of ViewEventTestBase must implement two methods:
+// . DoTestOnMessageLoop: invoked when the message loop is running. Run your
+// test here, invoke Done when done.
+// . CreateContentsView: returns the view to place in the window.
+//
+// Once you have created a ViewEventTestBase use the macro VIEW_TEST to define
+// the fixture.
+//
+// I encountered weird timing problems in initiating dragging and drop that
+// necessitated ugly hacks. In particular when the hook installed by
+// ui_controls received the mouse event and posted a task that task was not
+// processed. To work around this use the following pattern when initiating
+// dnd:
+// // Schedule the mouse move at a location slightly different from where
+// // you really want to move to.
+// ui_controls::SendMouseMoveNotifyWhenDone(loc.x + 10, loc.y,
+// NewRunnableMethod(this, YYY));
+// // Then use this to schedule another mouse move.
+// ScheduleMouseMoveInBackground(loc.x, loc.y);
+
+class ViewEventTestBase : public ChromeViews::WindowDelegate,
+ public testing::Test {
+ public:
+ // Invoke when done either because of failure or success. Quits the message
+ // loop.
+ static void Done();
+
+ ViewEventTestBase();
+
+ // Creates a window.
+ virtual void SetUp();
+
+ // Destroys the window.
+ virtual void TearDown();
+
+ virtual bool CanResize() const {
+ return true;
+ }
+
+ // WindowDelegate method. Calls into CreateContentsView to get the actual
+ // view.
+ virtual ChromeViews::View* GetContentsView();
+
+ // Overriden to do nothing so that this class can be used in runnable tasks.
+ void AddRef() {}
+ void Release() {}
+
+ protected:
+ // Returns the view that is added to the window.
+ virtual ChromeViews::View* CreateContentsView() = 0;
+
+ // Called once the message loop is running.
+ virtual void DoTestOnMessageLoop() = 0;
+
+ // Invoke from test main. Shows the window, starts the message loop and
+ // schedules a task that invokes DoTestOnMessageLoop.
+ void StartMessageLoopAndRunTest();
+
+ // Returns an empty Size. Subclasses that want a preferred size other than
+ // that of the View returned by CreateContentsView should override this
+ // appropriately.
+ virtual gfx::Size GetPreferredSize() { return gfx::Size(); }
+
+ // Creates a task that calls the specified method back. The specified
+ // method is called in such a way that if there are any test failures
+ // Done is invoked.
+ template <class T, class Method>
+ Task* CreateEventTask(T* target, Method method) {
+ return NewRunnableMethod(this, &ViewEventTestBase::RunTestMethod,
+ NewRunnableMethod(target, method));
+ }
+
+ // Spawns a new thread posts a MouseMove in the background.
+ void ScheduleMouseMoveInBackground(int x, int y);
+
+ ChromeViews::Window* window_;
+
+ private:
+ // Stops the thread started by ScheduleMouseMoveInBackground.
+ void StopBackgroundThread();
+
+ // Callback from CreateEventTask. Stops the background thread, runs the
+ // supplied task and if there are failures invokes Done.
+ void RunTestMethod(Task* task);
+
+ // The content of the Window.
+ ChromeViews::View* content_view_;
+
+ // Thread for posting background MouseMoves.
+ scoped_ptr<Thread> dnd_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewEventTestBase);
+};
+
+// Convenience macro for defining a ViewEventTestBase. See class description
+// of ViewEventTestBase for details.
+//
+// NOTE: These tests are disabled until we get a buildbot that is always logged
+// in and can run them.
+#define VIEW_TEST(test_class, name) \
+ TEST_F(test_class, DISABLED_name) {\
+ StartMessageLoopAndRunTest();\
+ }
+
+#endif // CHROME_TEST_UI_VIEW_EVENT_TEST_BASE_H_
diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj
index dfea76e..d69d0fc 100644
--- a/chrome/test/unit/unittests.vcproj
+++ b/chrome/test/unit/unittests.vcproj
@@ -194,6 +194,14 @@
>
</File>
<File
+ RelativePath="..\ui\view_event_test_base.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\ui\view_event_test_base.h"
+ >
+ </File>
+ <File
RelativePath="..\..\..\net\url_request\url_request_test_job.cc"
>
</File>
@@ -203,6 +211,14 @@
</File>
</Filter>
<Filter
+ Name="TestBookmarkBarView"
+ >
+ <File
+ RelativePath="..\..\browser\views\bookmark_bar_view_test.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
Name="TestGoogleURLTracker"
>
<File
diff --git a/chrome/views/chrome_menu.cc b/chrome/views/chrome_menu.cc
index 271c19b..a0c6abb 100644
--- a/chrome/views/chrome_menu.cc
+++ b/chrome/views/chrome_menu.cc
@@ -1072,6 +1072,9 @@ const int MenuItemView::kMenuItemViewID = 1001;
// static
const int MenuItemView::kDropBetweenPixels = 5;
+// static
+bool MenuItemView::allow_task_nesting_during_run_ = false;
+
MenuItemView::MenuItemView(MenuDelegate* delegate) {
DCHECK(delegate_);
Init(NULL, 0, SUBMENU, delegate);
@@ -1571,7 +1574,15 @@ MenuItemView* MenuController::Run(HWND parent,
DLOG(INFO) << " entering nested loop, depth=" << nested_depth;
#endif
- MessageLoop::current()->Run(this);
+ if (MenuItemView::allow_task_nesting_during_run_) {
+ bool did_allow_task_nesting =
+ MessageLoop::current()->NestableTasksAllowed();
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run(this);
+ MessageLoop::current()->SetNestableTasksAllowed(did_allow_task_nesting);
+ } else {
+ MessageLoop::current()->Run(this);
+ }
#ifdef DEBUG_MENU
nested_depth--;
diff --git a/chrome/views/chrome_menu.h b/chrome/views/chrome_menu.h
index 0c478aa..03f3c6c 100644
--- a/chrome/views/chrome_menu.h
+++ b/chrome/views/chrome_menu.h
@@ -237,6 +237,10 @@ class MenuItemView : public View {
// before/after the menu item, otherwise it is on the item.
static const int kDropBetweenPixels;
+ // If true SetNestableTasksAllowed(true) is invoked before MessageLoop::Run
+ // is invoked. This is only useful for testing and defaults to false.
+ static bool allow_task_nesting_during_run_;
+
// Different types of menu items.
enum Type {
NORMAL,
@@ -388,7 +392,7 @@ class MenuItemView : public View {
// Returns the root parent, or this if this has no parent.
MenuItemView* GetRootMenuItem();
- // Returs the mnemonic for this MenuItemView, or 0 if this MenuItemView
+ // Returns the mnemonic for this MenuItemView, or 0 if this MenuItemView
// doesn't have a mnemonic.
wchar_t GetMnemonic();
diff --git a/chrome/views/menu_button.cc b/chrome/views/menu_button.cc
index 1ebaba4..452fbac 100644
--- a/chrome/views/menu_button.cc
+++ b/chrome/views/menu_button.cc
@@ -70,7 +70,7 @@ MenuButton::MenuButton(const std::wstring& text,
bool show_menu_marker)
: TextButton(text),
menu_visible_(false),
- menu_closed_time_(Time::Now()),
+ menu_closed_time_(),
menu_delegate_(menu_delegate),
show_menu_marker_(show_menu_marker) {
if (kMenuMarker == NULL) {