// Copyright 2013 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.
#include "content/shell/browser/shell.h"
#include <windows.h>
#include <commctrl.h>
#include <fcntl.h>
#include <io.h>
#include "base/strings/utf_string_conversions.h"
#include "base/win/wrapped_window_proc.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "content/shell/app/resource.h"
#include "ui/gfx/win/hwnd_util.h"
namespace {
const wchar_t kWindowTitle[] = L"Content Shell";
const wchar_t kWindowClass[] = L"CONTENT_SHELL";
const int kButtonWidth = 72;
const int kURLBarHeight = 24;
const int kMaxURLLength = 1024;
} // namespace
namespace content {
HINSTANCE Shell::instance_handle_;
void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
_setmode(_fileno(stdout), _O_BINARY);
_setmode(_fileno(stderr), _O_BINARY);
INITCOMMONCONTROLSEX InitCtrlEx;
InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
InitCtrlEx.dwICC = ICC_STANDARD_CLASSES;
InitCommonControlsEx(&InitCtrlEx);
RegisterWindowClass();
}
void Shell::PlatformExit() {
std::vector<Shell*> windows = windows_;
for (std::vector<Shell*>::iterator it = windows.begin();
it != windows.end(); ++it)
DestroyWindow((*it)->window_);
}
void Shell::PlatformCleanUp() {
// When the window is destroyed, tell the Edit field to forget about us,
// otherwise we will crash.
gfx::SetWindowProc(url_edit_view_, default_edit_wnd_proc_);
gfx::SetWindowUserData(url_edit_view_, NULL);
}
void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
int id;
switch (control) {
case BACK_BUTTON:
id = IDC_NAV_BACK;
break;
case FORWARD_BUTTON:
id = IDC_NAV_FORWARD;
break;
case STOP_BUTTON:
id = IDC_NAV_STOP;
break;
default:
NOTREACHED() << "Unknown UI control";
return;
}
EnableWindow(GetDlgItem(window_, id), is_enabled);
}
void Shell::PlatformSetAddressBarURL(const GURL& url) {
std::wstring url_string = UTF8ToWide(url.spec());
SendMessage(url_edit_view_, WM_SETTEXT, 0,
reinterpret_cast<LPARAM>(url_string.c_str()));
}
void Shell::PlatformSetIsLoading(bool loading) {
}
void Shell::PlatformCreateWindow(int width, int height) {
window_ = CreateWindow(kWindowClass, kWindowTitle,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
NULL, NULL, instance_handle_, NULL);
gfx::SetWindowUserData(window_, this);
HWND hwnd;
int x = 0;
hwnd = CreateWindow(L"BUTTON", L"Back",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
x, 0, kButtonWidth, kURLBarHeight,
window_, (HMENU) IDC_NAV_BACK, instance_handle_, 0);
x += kButtonWidth;
hwnd = CreateWindow(L"BUTTON", L"Forward",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
x, 0, kButtonWidth, kURLBarHeight,
window_, (HMENU) IDC_NAV_FORWARD, instance_handle_, 0);
x += kButtonWidth;
hwnd = CreateWindow(L"BUTTON", L"Reload",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
x, 0, kButtonWidth, kURLBarHeight,
window_, (HMENU) IDC_NAV_RELOAD, instance_handle_, 0);
x += kButtonWidth;
hwnd = CreateWindow(L"BUTTON", L"Stop",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON ,
x, 0, kButtonWidth, kURLBarHeight,
window_, (HMENU) IDC_NAV_STOP, instance_handle_, 0);
x += kButtonWidth;
// This control is positioned by PlatformResizeSubViews.
url_edit_view_ = CreateWindow(L"EDIT", 0,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT |
ES_AUTOVSCROLL | ES_AUTOHSCROLL,
x, 0, 0, 0, window_, 0, instance_handle_, 0);
default_edit_wnd_proc_ = gfx::SetWindowProc(url_edit_view_,
Shell::EditWndProc);
gfx::SetWindowUserData(url_edit_view_, this);
ShowWindow(window_, SW_SHOW);
SizeTo(gfx::Size(width, height));
}
void Shell::PlatformSetContents() {
SetParent(web_contents_->GetView()->GetNativeView(), window_);
}
void Shell::SizeTo(const gfx::Size& size) {
RECT rc, rw;
GetClientRect(window_, &rc);
GetWindowRect(window_, &rw);
int client_width = rc.right - rc.left;
int window_width = rw.right - rw.left;
window_width = (window_width - client_width) + size.width();
int client_height = rc.bottom - rc.top;
int window_height = rw.bottom - rw.top;
window_height = (window_height - client_height) + size.height();
// Add space for the url bar.
window_height += kURLBarHeight;
SetWindowPos(window_, NULL, 0, 0, window_width, window_height,
SWP_NOMOVE | SWP_NOZORDER);
}
void Shell::PlatformResizeSubViews() {
RECT rc;
GetClientRect(window_, &rc);
int x = kButtonWidth * 4;
MoveWindow(url_edit_view_, x, 0, rc.right - x, kURLBarHeight, TRUE);
MoveWindow(GetContentView(), 0, kURLBarHeight, rc.right,
rc.bottom - kURLBarHeight, TRUE);
}
void Shell::Close() {
DestroyWindow(window_);
}
ATOM Shell::RegisterWindowClass() {
WNDCLASSEX window_class;
base::win::InitializeWindowClass(
kWindowClass,
&Shell::WndProc,
CS_HREDRAW | CS_VREDRAW,
0,
0,
LoadCursor(NULL, IDC_ARROW),
NULL,
MAKEINTRESOURCE(IDC_CONTENTSHELL),
NULL,
NULL,
&window_class);
instance_handle_ = window_class.hInstance;
return RegisterClassEx(&window_class);
}
LRESULT CALLBACK Shell::WndProc(HWND hwnd, UINT message, WPARAM wParam,
LPARAM lParam) {
Shell* shell = static_cast<Shell*>(gfx::GetWindowUserData(hwnd));
switch (message) {
case WM_COMMAND: {
int id = LOWORD(wParam);
switch (id) {
case IDM_NEW_WINDOW:
CreateNewWindow(
shell->web_contents()->GetBrowserContext(),
GURL(), NULL, MSG_ROUTING_NONE, gfx::Size());
break;
case IDM_CLOSE_WINDOW:
DestroyWindow(hwnd);
break;
case IDM_EXIT:
PlatformExit();
break;
case IDM_SHOW_DEVELOPER_TOOLS:
shell->ShowDevTools();
break;
case IDC_NAV_BACK:
shell->GoBackOrForward(-1);
break;
case IDC_NAV_FORWARD:
shell->GoBackOrForward(1);
break;
case IDC_NAV_RELOAD:
shell->Reload();
break;
case IDC_NAV_STOP:
shell->Stop();
break;
}
break;
}
case WM_DESTROY: {
delete shell;
return 0;
}
case WM_SIZE: {
if (shell->GetContentView())
shell->PlatformResizeSubViews();
return 0;
}
case WM_WINDOWPOSCHANGED: {
// Notify the content view that the window position of its parent window
// has been changed by sending window message
gfx::NativeView native_view = shell->GetContentView();
if (native_view) {
SendMessage(native_view, message, wParam, lParam);
}
break;
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK Shell::EditWndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam) {
Shell* shell = static_cast<Shell*>(gfx::GetWindowUserData(hwnd));
switch (message) {
case WM_CHAR:
if (wParam == VK_RETURN) {
wchar_t str[kMaxURLLength + 1]; // Leave room for adding a NULL;
*(str) = kMaxURLLength;
LRESULT str_len = SendMessage(hwnd, EM_GETLINE, 0, (LPARAM)str);
if (str_len > 0) {
str[str_len] = 0; // EM_GETLINE doesn't NULL terminate.
GURL url(str);
if (!url.has_scheme())
url = GURL(std::wstring(L"http://") + std::wstring(str));
shell->LoadURL(url);
}
return 0;
}
}
return CallWindowProc(shell->default_edit_wnd_proc_, hwnd, message, wParam,
lParam);
}
void Shell::PlatformSetTitle(const base::string16& text) {
::SetWindowText(window_, text.c_str());
}
} // namespace content