1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <atlbase.h>
6#include <atlapp.h>
7#include <atlmisc.h>
8#include <atlwin.h>
9
10#include "testing/gtest/include/gtest/gtest.h"
11#include "testing/gmock/include/gmock/gmock.h"
12
13#include "base/logging.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/win/registry.h"
16#include "chrome_frame/infobars/infobar_content.h"
17#include "chrome_frame/ready_mode/internal/ready_mode_state.h"
18#include "chrome_frame/ready_mode/internal/ready_prompt_content.h"
19#include "chrome_frame/ready_mode/internal/ready_prompt_window.h"
20#include "chrome_frame/ready_mode/internal/registry_ready_mode_state.h"
21#include "chrome_frame/ready_mode/internal/url_launcher.h"
22#include "chrome_frame/simple_resource_loader.h"
23#include "chrome_frame/test/chrome_frame_test_utils.h"
24
25namespace {
26
27class SetResourceInstance {
28 public:
29  SetResourceInstance() : res_dll_(NULL), old_res_dll_(NULL) {
30    SimpleResourceLoader* loader_instance = SimpleResourceLoader::GetInstance();
31    EXPECT_TRUE(loader_instance != NULL);
32    if (loader_instance != NULL) {
33      res_dll_ = loader_instance->GetResourceModuleHandle();
34      EXPECT_TRUE(res_dll_ != NULL);
35      if (res_dll_ != NULL) {
36        old_res_dll_ = ATL::_AtlBaseModule.SetResourceInstance(res_dll_);
37      }
38    }
39  }
40
41  ~SetResourceInstance() {
42    if (old_res_dll_ != NULL) {
43      CHECK_EQ(res_dll_, ATL::_AtlBaseModule.SetResourceInstance(old_res_dll_));
44    }
45  }
46
47 private:
48  HMODULE res_dll_;
49  HMODULE old_res_dll_;
50};  // class SetResourceInstance
51
52class SimpleWindow : public CWindowImpl<SimpleWindow,
53                                        CWindow,
54                                        CFrameWinTraits> {
55 public:
56  virtual ~SimpleWindow() {
57    if (IsWindow())
58      DestroyWindow();
59  }
60
61  static BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM l_param) {
62    HWND* out = reinterpret_cast<HWND*>(l_param);
63    EXPECT_TRUE(out != NULL);
64
65    if (out == NULL)
66      return FALSE;
67
68    EXPECT_TRUE(*out == NULL || ::IsChild(*out, hwnd));
69
70    if (*out == NULL)
71      *out = hwnd;
72
73    return TRUE;
74  }
75
76  HWND GetZeroOrOneChildWindows() {
77    HWND child = NULL;
78    EnumChildWindows(m_hWnd, EnumChildProc, reinterpret_cast<LPARAM>(&child));
79    return child;
80  }
81
82  BEGIN_MSG_MAP(SimpleWindow)
83  END_MSG_MAP()
84};  // class SimpleWindow
85
86class MockInfobarContentFrame : public InfobarContent::Frame {
87 public:
88  // InfobarContent::Frame implementation
89  MOCK_METHOD0(GetFrameWindow, HWND(void));
90  MOCK_METHOD0(CloseInfobar, void(void));
91};  // class Frame
92
93class MockReadyModeState : public ReadyModeState {
94 public:
95  // ReadyModeState implementation
96  MOCK_METHOD0(TemporarilyDeclineChromeFrame, void(void));
97  MOCK_METHOD0(PermanentlyDeclineChromeFrame, void(void));
98  MOCK_METHOD0(AcceptChromeFrame, void(void));
99};  // class MockReadyModeState
100
101class MockUrlLauncher : public UrlLauncher {
102 public:
103  // UrlLauncher implementation
104  MOCK_METHOD1(LaunchUrl, void(const std::wstring& url));
105};  // class MockUrlLauncher
106
107}  // namespace
108
109class ReadyPromptTest : public testing::Test {
110 public:
111  ReadyPromptTest() : hwnd_(NULL) {}
112
113  void SetUp() {
114    hwnd_ = window_.Create(NULL);
115    EXPECT_TRUE(hwnd_ != NULL);
116    window_.ShowWindow(SW_SHOW);
117    EXPECT_TRUE(window_.IsWindowVisible());
118    EXPECT_CALL(frame_, GetFrameWindow()).Times(testing::AnyNumber())
119        .WillRepeatedly(testing::Return(hwnd_));
120  }
121
122 protected:
123  SimpleWindow window_;
124  HWND hwnd_;
125  MockInfobarContentFrame frame_;
126  SetResourceInstance set_resource_instance_;
127};  // class ReadyPromptTest
128
129class ReadyPromptWindowTest : public ReadyPromptTest {
130 public:
131  void SetUp() {
132    ReadyPromptTest::SetUp();
133
134    // owned by ReadyPromptWindow
135    state_ = new MockReadyModeState();
136    url_launcher_ = new MockUrlLauncher();
137
138    ready_prompt_window_ = ReadyPromptWindow::CreateInstance(
139        &frame_, state_, url_launcher_);
140
141    ASSERT_TRUE(ready_prompt_window_ != NULL);
142    RECT position = {0, 0, 800, 39};
143    ASSERT_TRUE(ready_prompt_window_->SetWindowPos(HWND_TOP, &position,
144                                                   SWP_SHOWWINDOW));
145  }
146
147 protected:
148  MockReadyModeState* state_;
149  MockUrlLauncher* url_launcher_;
150  base::WeakPtr<ReadyPromptWindow> ready_prompt_window_;
151};  // class ReadyPromptWindowTest
152
153class ReadyPromptWindowButtonTest : public ReadyPromptWindowTest {
154 public:
155  void TearDown() {
156    ASSERT_TRUE(ready_prompt_window_ != NULL);
157    ASSERT_TRUE(ready_prompt_window_->DestroyWindow());
158    ASSERT_TRUE(ready_prompt_window_ == NULL);
159    ASSERT_FALSE(message_loop_.WasTimedOut());
160
161    ReadyPromptWindowTest::TearDown();
162  }
163
164 protected:
165  struct ClickOnCaptionData {
166    const wchar_t* target_caption;
167    bool found;
168  };  // struct ClickOnCaptionData
169
170  static BOOL CALLBACK ClickOnCaptionProc(HWND hwnd, LPARAM l_param) {
171    wchar_t window_caption[256] = {0};
172    size_t buffer_length = arraysize(window_caption);
173
174    ClickOnCaptionData* data = reinterpret_cast<ClickOnCaptionData*>(l_param);
175    EXPECT_TRUE(data->target_caption != NULL);
176
177    if (data->target_caption == NULL)
178      return FALSE;
179
180    if (wcsnlen(data->target_caption, buffer_length + 1) == buffer_length + 1)
181      return FALSE;
182
183    if (::GetWindowText(hwnd, window_caption, buffer_length) ==
184        static_cast<int>(buffer_length)) {
185      return TRUE;
186    }
187
188    if (wcscmp(data->target_caption, window_caption) == 0) {
189      EXPECT_FALSE(data->found);
190
191      CRect client_rect;
192      EXPECT_TRUE(::GetClientRect(hwnd, client_rect));
193
194      CPoint center_point(client_rect.CenterPoint());
195      LPARAM coordinates = (center_point.y << 16) | center_point.x;
196
197      ::PostMessage(hwnd, WM_LBUTTONDOWN, 0, coordinates);
198      ::PostMessage(hwnd, WM_LBUTTONUP, 0, coordinates);
199
200      data->found = true;
201    }
202
203    return TRUE;
204  }
205
206  bool ClickOnCaption(const std::wstring& caption) {
207    ClickOnCaptionData data = {caption.c_str(), false};
208
209    ::EnumChildWindows(hwnd_, ClickOnCaptionProc,
210                       reinterpret_cast<LPARAM>(&data));
211    return data.found;
212  }
213
214  void RunUntilCloseInfobar() {
215    EXPECT_CALL(frame_, CloseInfobar()).WillOnce(QUIT_LOOP(message_loop_));
216    ASSERT_NO_FATAL_FAILURE(message_loop_.RunFor(
217        base::TimeDelta::FromSeconds(5)));
218  }
219
220  chrome_frame_test::TimedMsgLoop message_loop_;
221};  // class ReadyPromptWindowButtonTest
222
223TEST_F(ReadyPromptTest, ReadyPromptContentTest) {
224  // owned by ReadyPromptContent
225  MockReadyModeState* state = new MockReadyModeState();
226  MockUrlLauncher* url_launcher = new MockUrlLauncher();
227
228  scoped_ptr<ReadyPromptContent> content_(new ReadyPromptContent(state,
229                                                                 url_launcher));
230
231  content_->InstallInFrame(&frame_);
232
233  // Ensure that, if a child is created, it is not visible yet.
234  HWND child_hwnd = window_.GetZeroOrOneChildWindows();
235  if (child_hwnd != NULL) {
236    CWindow child(child_hwnd);
237    RECT child_dimensions;
238    EXPECT_TRUE(child.GetClientRect(&child_dimensions));
239    EXPECT_FALSE(child.IsWindowVisible() && !::IsRectEmpty(&child_dimensions));
240  }
241
242  int desired_height = content_->GetDesiredSize(400, 0);
243  EXPECT_GT(desired_height, 0);
244  RECT dimensions = {10, 15, 410, 20};
245  content_->SetDimensions(dimensions);
246
247  child_hwnd = window_.GetZeroOrOneChildWindows();
248  EXPECT_TRUE(child_hwnd != NULL);
249
250  if (child_hwnd != NULL) {
251    CWindow child(child_hwnd);
252    EXPECT_TRUE(child.IsWindowVisible());
253    RECT child_dimensions;
254    EXPECT_TRUE(child.GetWindowRect(&child_dimensions));
255    EXPECT_TRUE(window_.ScreenToClient(&child_dimensions));
256    EXPECT_TRUE(::EqualRect(&child_dimensions, &dimensions));
257  }
258
259  // Being visible doesn't change the desired height
260  EXPECT_EQ(desired_height, content_->GetDesiredSize(400, 0));
261
262  content_.reset();
263
264  EXPECT_TRUE(window_.GetZeroOrOneChildWindows() == NULL);
265}
266
267TEST_F(ReadyPromptWindowTest, Destroy) {
268  // Should delete associated mocks, not invoke on ReadyModeState
269  ready_prompt_window_->DestroyWindow();
270}
271
272TEST_F(ReadyPromptWindowButtonTest, ClickEnable) {
273  EXPECT_CALL(*state_, AcceptChromeFrame());
274  ASSERT_TRUE(ClickOnCaption(L"Enable"));
275  RunUntilCloseInfobar();
276}
277
278TEST_F(ReadyPromptWindowButtonTest, ClickIgnore) {
279  EXPECT_CALL(*state_, PermanentlyDeclineChromeFrame());
280  ASSERT_TRUE(ClickOnCaption(L"Ignore"));
281  RunUntilCloseInfobar();
282}
283
284// TODO(erikwright): test WebBrowserAdapter
285// TODO(erikwright): an integration test of ReadyMode::Configure with a mock
286//                   IWebBrowser2?
287