1// Copyright 2013 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 "content/shell/browser/shell.h"
6
7#include <windows.h>
8#include <commctrl.h>
9#include <fcntl.h>
10#include <io.h>
11
12#include "base/strings/utf_string_conversions.h"
13#include "base/win/wrapped_window_proc.h"
14#include "content/public/browser/web_contents.h"
15#include "content/public/browser/web_contents_view.h"
16#include "content/shell/app/resource.h"
17#include "ui/gfx/win/hwnd_util.h"
18
19namespace {
20
21const wchar_t kWindowTitle[] = L"Content Shell";
22const wchar_t kWindowClass[] = L"CONTENT_SHELL";
23
24const int kButtonWidth = 72;
25const int kURLBarHeight = 24;
26
27const int kMaxURLLength = 1024;
28
29}  // namespace
30
31namespace content {
32
33HINSTANCE Shell::instance_handle_;
34
35void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
36  _setmode(_fileno(stdout), _O_BINARY);
37  _setmode(_fileno(stderr), _O_BINARY);
38  INITCOMMONCONTROLSEX InitCtrlEx;
39  InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
40  InitCtrlEx.dwICC  = ICC_STANDARD_CLASSES;
41  InitCommonControlsEx(&InitCtrlEx);
42  RegisterWindowClass();
43}
44
45void Shell::PlatformExit() {
46  std::vector<Shell*> windows = windows_;
47  for (std::vector<Shell*>::iterator it = windows.begin();
48       it != windows.end(); ++it)
49    DestroyWindow((*it)->window_);
50}
51
52void Shell::PlatformCleanUp() {
53  // When the window is destroyed, tell the Edit field to forget about us,
54  // otherwise we will crash.
55  gfx::SetWindowProc(url_edit_view_, default_edit_wnd_proc_);
56  gfx::SetWindowUserData(url_edit_view_, NULL);
57}
58
59void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
60  int id;
61  switch (control) {
62    case BACK_BUTTON:
63      id = IDC_NAV_BACK;
64      break;
65    case FORWARD_BUTTON:
66      id = IDC_NAV_FORWARD;
67      break;
68    case STOP_BUTTON:
69      id = IDC_NAV_STOP;
70      break;
71    default:
72      NOTREACHED() << "Unknown UI control";
73      return;
74  }
75  EnableWindow(GetDlgItem(window_, id), is_enabled);
76}
77
78void Shell::PlatformSetAddressBarURL(const GURL& url) {
79  std::wstring url_string = UTF8ToWide(url.spec());
80  SendMessage(url_edit_view_, WM_SETTEXT, 0,
81              reinterpret_cast<LPARAM>(url_string.c_str()));
82}
83
84void Shell::PlatformSetIsLoading(bool loading) {
85}
86
87void Shell::PlatformCreateWindow(int width, int height) {
88  window_ = CreateWindow(kWindowClass, kWindowTitle,
89                         WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
90                         CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
91                         NULL, NULL, instance_handle_, NULL);
92  gfx::SetWindowUserData(window_, this);
93
94  HWND hwnd;
95  int x = 0;
96
97  hwnd = CreateWindow(L"BUTTON", L"Back",
98                      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
99                      x, 0, kButtonWidth, kURLBarHeight,
100                      window_, (HMENU) IDC_NAV_BACK, instance_handle_, 0);
101  x += kButtonWidth;
102
103  hwnd = CreateWindow(L"BUTTON", L"Forward",
104                      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
105                      x, 0, kButtonWidth, kURLBarHeight,
106                      window_, (HMENU) IDC_NAV_FORWARD, instance_handle_, 0);
107  x += kButtonWidth;
108
109  hwnd = CreateWindow(L"BUTTON", L"Reload",
110                      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
111                      x, 0, kButtonWidth, kURLBarHeight,
112                      window_, (HMENU) IDC_NAV_RELOAD, instance_handle_, 0);
113  x += kButtonWidth;
114
115  hwnd = CreateWindow(L"BUTTON", L"Stop",
116                      WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
117                      x, 0, kButtonWidth, kURLBarHeight,
118                      window_, (HMENU) IDC_NAV_STOP, instance_handle_, 0);
119  x += kButtonWidth;
120
121  // This control is positioned by PlatformResizeSubViews.
122  url_edit_view_ = CreateWindow(L"EDIT", 0,
123                                WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
124                                ES_AUTOVSCROLL | ES_AUTOHSCROLL,
125                                x, 0, 0, 0, window_, 0, instance_handle_, 0);
126
127  default_edit_wnd_proc_ = gfx::SetWindowProc(url_edit_view_,
128                                             Shell::EditWndProc);
129  gfx::SetWindowUserData(url_edit_view_, this);
130
131  ShowWindow(window_, SW_SHOW);
132
133  SizeTo(gfx::Size(width, height));
134}
135
136void Shell::PlatformSetContents() {
137  SetParent(web_contents_->GetView()->GetNativeView(), window_);
138}
139
140void Shell::SizeTo(const gfx::Size& size) {
141  RECT rc, rw;
142  GetClientRect(window_, &rc);
143  GetWindowRect(window_, &rw);
144
145  int client_width = rc.right - rc.left;
146  int window_width = rw.right - rw.left;
147  window_width = (window_width - client_width) + size.width();
148
149  int client_height = rc.bottom - rc.top;
150  int window_height = rw.bottom - rw.top;
151  window_height = (window_height - client_height) + size.height();
152
153  // Add space for the url bar.
154  window_height += kURLBarHeight;
155
156  SetWindowPos(window_, NULL, 0, 0, window_width, window_height,
157               SWP_NOMOVE | SWP_NOZORDER);
158}
159
160void Shell::PlatformResizeSubViews() {
161  RECT rc;
162  GetClientRect(window_, &rc);
163
164  int x = kButtonWidth * 4;
165  MoveWindow(url_edit_view_, x, 0, rc.right - x, kURLBarHeight, TRUE);
166
167  MoveWindow(GetContentView(), 0, kURLBarHeight, rc.right,
168             rc.bottom - kURLBarHeight, TRUE);
169}
170
171void Shell::Close() {
172  DestroyWindow(window_);
173}
174
175ATOM Shell::RegisterWindowClass() {
176  WNDCLASSEX window_class;
177  base::win::InitializeWindowClass(
178      kWindowClass,
179      &Shell::WndProc,
180      CS_HREDRAW | CS_VREDRAW,
181      0,
182      0,
183      LoadCursor(NULL, IDC_ARROW),
184      NULL,
185      MAKEINTRESOURCE(IDC_CONTENTSHELL),
186      NULL,
187      NULL,
188      &window_class);
189  instance_handle_ = window_class.hInstance;
190  return RegisterClassEx(&window_class);
191}
192
193LRESULT CALLBACK Shell::WndProc(HWND hwnd, UINT message, WPARAM wParam,
194                                LPARAM lParam) {
195  Shell* shell = static_cast<Shell*>(gfx::GetWindowUserData(hwnd));
196
197  switch (message) {
198    case WM_COMMAND: {
199      int id = LOWORD(wParam);
200      switch (id) {
201        case IDM_NEW_WINDOW:
202          CreateNewWindow(
203              shell->web_contents()->GetBrowserContext(),
204              GURL(), NULL, MSG_ROUTING_NONE, gfx::Size());
205          break;
206        case IDM_CLOSE_WINDOW:
207          DestroyWindow(hwnd);
208          break;
209        case IDM_EXIT:
210          PlatformExit();
211          break;
212        case IDM_SHOW_DEVELOPER_TOOLS:
213          shell->ShowDevTools();
214          break;
215        case IDC_NAV_BACK:
216          shell->GoBackOrForward(-1);
217          break;
218        case IDC_NAV_FORWARD:
219          shell->GoBackOrForward(1);
220          break;
221        case IDC_NAV_RELOAD:
222          shell->Reload();
223          break;
224        case IDC_NAV_STOP:
225          shell->Stop();
226          break;
227      }
228      break;
229    }
230    case WM_DESTROY: {
231      delete shell;
232      return 0;
233    }
234
235    case WM_SIZE: {
236      if (shell->GetContentView())
237        shell->PlatformResizeSubViews();
238      return 0;
239    }
240
241    case WM_WINDOWPOSCHANGED: {
242      // Notify the content view that the window position of its parent window
243      // has been changed by sending window message
244      gfx::NativeView native_view = shell->GetContentView();
245      if (native_view) {
246        SendMessage(native_view, message, wParam, lParam);
247      }
248      break;
249   }
250  }
251
252  return DefWindowProc(hwnd, message, wParam, lParam);
253}
254
255LRESULT CALLBACK Shell::EditWndProc(HWND hwnd, UINT message,
256                                    WPARAM wParam, LPARAM lParam) {
257  Shell* shell = static_cast<Shell*>(gfx::GetWindowUserData(hwnd));
258
259  switch (message) {
260    case WM_CHAR:
261      if (wParam == VK_RETURN) {
262        wchar_t str[kMaxURLLength + 1];  // Leave room for adding a NULL;
263        *(str) = kMaxURLLength;
264        LRESULT str_len = SendMessage(hwnd, EM_GETLINE, 0, (LPARAM)str);
265        if (str_len > 0) {
266          str[str_len] = 0;  // EM_GETLINE doesn't NULL terminate.
267          GURL url(str);
268          if (!url.has_scheme())
269            url = GURL(std::wstring(L"http://") + std::wstring(str));
270          shell->LoadURL(url);
271        }
272
273        return 0;
274      }
275  }
276
277  return CallWindowProc(shell->default_edit_wnd_proc_, hwnd, message, wParam,
278                        lParam);
279}
280
281void Shell::PlatformSetTitle(const base::string16& text) {
282  ::SetWindowText(window_, text.c_str());
283}
284
285}  // namespace content
286