diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-25 17:40:21 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-25 17:40:21 +0000 |
commit | 59c9f17a96e85f4589483a0133715ab373700930 (patch) | |
tree | f3d6879a44fb40288c06d22f14cc1fdfc4247917 /content | |
parent | 482853dcc4fd22bb0b27e75a9b0f9b350c58d731 (diff) | |
download | chromium_src-59c9f17a96e85f4589483a0133715ab373700930.zip chromium_src-59c9f17a96e85f4589483a0133715ab373700930.tar.gz chromium_src-59c9f17a96e85f4589483a0133715ab373700930.tar.bz2 |
Move RenderWidgetHostViewWin to content.
BUG=76697
Review URL: http://codereview.chromium.org/7745024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98265 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r-- | content/DEPS | 2 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_view_win.cc | 1863 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_view_win.h | 389 | ||||
-rw-r--r-- | content/content_browser.gypi | 2 |
4 files changed, 2256 insertions, 0 deletions
diff --git a/content/DEPS b/content/DEPS index 01fd1fa..24476e0 100644 --- a/content/DEPS +++ b/content/DEPS @@ -46,6 +46,8 @@ include_rules = [ # given its id is left to the embedder. "-ui/base/l10n", "-ui/base/resource", + # This file isn't related to grd, so it's fine. + "+ui/base/l10n/l10n_util_win.h", "+views", "+webkit", diff --git a/content/browser/renderer_host/render_widget_host_view_win.cc b/content/browser/renderer_host/render_widget_host_view_win.cc new file mode 100644 index 0000000..97ef89d --- /dev/null +++ b/content/browser/renderer_host/render_widget_host_view_win.cc @@ -0,0 +1,1863 @@ +// Copyright (c) 2011 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. + +// Need Win 7 headers for WM_GESTURE and ChangeWindowMessageFilterEx +// TODO(jschuh): See crbug.com/92941 for longterm fix. +#undef WINVER +#define WINVER _WIN32_WINNT_WIN7 +#undef _WIN32_WINNT +#define _WIN32_WINNT _WIN32_WINNT_WIN7 +#include <windows.h> + +#include "content/browser/renderer_host/render_widget_host_view_win.h" + +#include <algorithm> + +#include "base/command_line.h" +#include "base/i18n/rtl.h" +#include "base/metrics/histogram.h" +#include "base/process_util.h" +#include "base/threading/thread.h" +#include "base/win/scoped_comptr.h" +#include "base/win/scoped_gdi_object.h" +#include "base/win/wrapped_window_proc.h" +#include "content/browser/accessibility/browser_accessibility_manager.h" +#include "content/browser/accessibility/browser_accessibility_state.h" +#include "content/browser/accessibility/browser_accessibility_win.h" +#include "content/browser/browser_thread.h" +#include "content/browser/content_browser_client.h" +#include "content/browser/plugin_process_host.h" +#include "content/browser/renderer_host/backing_store.h" +#include "content/browser/renderer_host/backing_store_win.h" +#include "content/browser/renderer_host/render_process_host.h" +#include "content/browser/renderer_host/render_widget_host.h" +#include "content/common/content_switches.h" +#include "content/common/native_web_keyboard_event.h" +#include "content/common/notification_service.h" +#include "content/common/plugin_messages.h" +#include "content/common/view_messages.h" +#include "skia/ext/skia_utils_win.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositionUnderline.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/win/WebInputEventFactory.h" +#include "ui/base/ime/composition_text.h" +#include "ui/base/l10n/l10n_util_win.h" +#include "ui/base/text/text_elider.h" +#include "ui/base/view_prop.h" +#include "ui/base/win/hwnd_util.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/canvas_skia.h" +#include "ui/gfx/gdi_util.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/screen.h" +#include "views/accessibility/native_view_accessibility_win.h" +#include "views/focus/focus_manager.h" +#include "views/focus/focus_util_win.h" +// Included for views::kReflectedMessage - TODO(beng): move this to win_util.h! +#include "views/widget/native_widget_win.h" +#include "webkit/glue/webaccessibility.h" +#include "webkit/glue/webcursor.h" +#include "webkit/plugins/npapi/plugin_constants_win.h" +#include "webkit/plugins/npapi/webplugin.h" +#include "webkit/plugins/npapi/webplugin_delegate_impl.h" + +using base::TimeDelta; +using base::TimeTicks; +using ui::ViewProp; +using WebKit::WebInputEvent; +using WebKit::WebInputEventFactory; +using WebKit::WebMouseEvent; +using WebKit::WebTextDirection; +using webkit::npapi::WebPluginGeometry; + +const wchar_t kRenderWidgetHostHWNDClass[] = L"Chrome_RenderWidgetHostHWND"; + +namespace { + +// Tooltips will wrap after this width. Yes, wrap. Imagine that! +const int kTooltipMaxWidthPixels = 300; + +// Maximum number of characters we allow in a tooltip. +const int kMaxTooltipLength = 1024; + +// A custom MSAA object id used to determine if a screen reader is actively +// listening for MSAA events. +const int kIdCustom = 1; + +// The delay before the compositor host window is destroyed. This gives the GPU +// process a grace period to stop referencing it. +const int kDestroyCompositorHostWindowDelay = 10000; + +const char* const kRenderWidgetHostViewKey = "__RENDER_WIDGET_HOST_VIEW__"; + +// A callback function for EnumThreadWindows to enumerate and dismiss +// any owned popop windows +BOOL CALLBACK DismissOwnedPopups(HWND window, LPARAM arg) { + const HWND toplevel_hwnd = reinterpret_cast<HWND>(arg); + + if (::IsWindowVisible(window)) { + const HWND owner = ::GetWindow(window, GW_OWNER); + if (toplevel_hwnd == owner) { + ::PostMessage(window, WM_CANCELMODE, 0, 0); + } + } + + return TRUE; +} + +class NotifyPluginProcessHostTask : public Task { + public: + NotifyPluginProcessHostTask(HWND window, HWND parent) + : window_(window), parent_(parent), tries_(kMaxTries) { } + + private: + void Run() { + DWORD plugin_process_id; + bool found_starting_plugin_process = false; + GetWindowThreadProcessId(window_, &plugin_process_id); + for (BrowserChildProcessHost::Iterator iter( + ChildProcessInfo::PLUGIN_PROCESS); + !iter.Done(); ++iter) { + PluginProcessHost* plugin = static_cast<PluginProcessHost*>(*iter); + if (!plugin->handle()) { + found_starting_plugin_process = true; + continue; + } + if (base::GetProcId(plugin->handle()) == plugin_process_id) { + plugin->AddWindow(parent_); + return; + } + } + + if (found_starting_plugin_process) { + // A plugin process has started but we don't have its handle yet. Since + // it's most likely the one for this plugin, try a few more times after a + // delay. + if (tries_--) { + MessageLoop::current()->PostDelayedTask(FROM_HERE, this, kTryDelayMs); + return; + } + } + + // The plugin process might have died in the time to execute the task, don't + // leak the HWND. + PostMessage(parent_, WM_CLOSE, 0, 0); + } + + HWND window_; // Plugin HWND, created and destroyed in the plugin process. + HWND parent_; // Parent HWND, created and destroyed on the browser UI thread. + + int tries_; + + // How many times we try to find a PluginProcessHost whose process matches + // the HWND. + static const int kMaxTries = 5; + // How long to wait between each try. + static const int kTryDelayMs = 200; +}; + +// Windows callback for OnDestroy to detach the plugin windows. +BOOL CALLBACK DetachPluginWindowsCallback(HWND window, LPARAM param) { + if (webkit::npapi::WebPluginDelegateImpl::IsPluginDelegateWindow(window) && + !IsHungAppWindow(window)) { + ::ShowWindow(window, SW_HIDE); + SetParent(window, NULL); + } + return TRUE; +} + +// Draw the contents of |backing_store_dc| onto |paint_rect| with a 70% grey +// filter. +void DrawDeemphasized(const SkColor& color, + const gfx::Rect& paint_rect, + HDC backing_store_dc, + HDC paint_dc) { + gfx::CanvasSkia canvas(paint_rect.width(), paint_rect.height(), true); + { + skia::ScopedPlatformPaint scoped_platform_paint(&canvas); + HDC dc = scoped_platform_paint.GetPlatformSurface(); + BitBlt(dc, + 0, + 0, + paint_rect.width(), + paint_rect.height(), + backing_store_dc, + paint_rect.x(), + paint_rect.y(), + SRCCOPY); + } + canvas.FillRectInt(color, 0, 0, paint_rect.width(), paint_rect.height()); + skia::DrawToNativeContext(&canvas, paint_dc, paint_rect.x(), + paint_rect.y(), NULL); +} + +// The plugin wrapper window which lives in the browser process has this proc +// as its window procedure. We only handle the WM_PARENTNOTIFY message sent by +// windowed plugins for mouse input. This is forwarded off to the wrappers +// parent which is typically the RVH window which turns on user gesture. +LRESULT CALLBACK PluginWrapperWindowProc(HWND window, unsigned int message, + WPARAM wparam, LPARAM lparam) { + if (message == WM_PARENTNOTIFY) { + switch (LOWORD(wparam)) { + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + ::SendMessage(GetParent(window), message, wparam, lparam); + return 0; + default: + break; + } + } + return ::DefWindowProc(window, message, wparam, lparam); +} + +// Must be dynamically loaded to avoid startup failures on Win XP. +typedef BOOL (WINAPI *ChangeWindowMessageFilterExFunction)( + HWND hwnd, + UINT message, + DWORD action, + PCHANGEFILTERSTRUCT change_filter_struct); +ChangeWindowMessageFilterExFunction g_ChangeWindowMessageFilterEx; + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostViewWin, public: + +RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget) + : render_widget_host_(widget), + compositor_host_window_(NULL), + hide_compositor_window_at_next_paint_(false), + track_mouse_leave_(false), + ime_notification_(false), + capture_enter_key_(false), + is_hidden_(false), + about_to_validate_and_paint_(false), + close_on_deactivate_(false), + being_destroyed_(false), + tooltip_hwnd_(NULL), + tooltip_showing_(false), + shutdown_factory_(this), + parent_hwnd_(NULL), + is_loading_(false), + overlay_color_(0), + text_input_type_(ui::TEXT_INPUT_TYPE_NONE), + is_fullscreen_(false) { + render_widget_host_->SetView(this); + registrar_.Add(this, + content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, + NotificationService::AllSources()); +} + +RenderWidgetHostViewWin::~RenderWidgetHostViewWin() { + ResetTooltip(); +} + +void RenderWidgetHostViewWin::CreateWnd(HWND parent) { + Create(parent); // ATL function to create the window. +} + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostViewWin, RenderWidgetHostView implementation: + +void RenderWidgetHostViewWin::InitAsPopup( + RenderWidgetHostView* parent_host_view, const gfx::Rect& pos) { + close_on_deactivate_ = true; + DoPopupOrFullscreenInit(parent_host_view->GetNativeView(), pos, + WS_EX_TOOLWINDOW); +} + +void RenderWidgetHostViewWin::InitAsFullscreen( + RenderWidgetHostView* reference_host_view) { + gfx::Rect pos = gfx::Screen::GetMonitorAreaNearestWindow( + reference_host_view->GetNativeView()); + is_fullscreen_ = true; + DoPopupOrFullscreenInit(GetDesktopWindow(), pos, 0); +} + +RenderWidgetHost* RenderWidgetHostViewWin::GetRenderWidgetHost() const { + return render_widget_host_; +} + +void RenderWidgetHostViewWin::DidBecomeSelected() { + if (!is_hidden_) + return; + + if (tab_switch_paint_time_.is_null()) + tab_switch_paint_time_ = TimeTicks::Now(); + is_hidden_ = false; + EnsureTooltip(); + render_widget_host_->WasRestored(); +} + +void RenderWidgetHostViewWin::WasHidden() { + if (is_hidden_) + return; + + // If we receive any more paint messages while we are hidden, we want to + // ignore them so we don't re-allocate the backing store. We will paint + // everything again when we become selected again. + is_hidden_ = true; + + ResetTooltip(); + + // If we have a renderer, then inform it that we are being hidden so it can + // reduce its resource utilization. + render_widget_host_->WasHidden(); + + // TODO(darin): what about constrained windows? it doesn't look like they + // see a message when their parent is hidden. maybe there is something more + // generic we can do at the TabContents API level instead of relying on + // Windows messages. +} + +void RenderWidgetHostViewWin::SetSize(const gfx::Size& size) { + SetBounds(gfx::Rect(GetViewBounds().origin(), size)); +} + +void RenderWidgetHostViewWin::SetBounds(const gfx::Rect& rect) { + if (is_hidden_) + return; + + // No SWP_NOREDRAW as autofill popups can move and the underneath window + // should redraw in that case. + UINT swp_flags = SWP_NOSENDCHANGING | SWP_NOOWNERZORDER | SWP_NOCOPYBITS | + SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE; + + // If the style is not popup, you have to convert the point to client + // coordinate. + POINT point = { rect.x(), rect.y() }; + if (GetStyle() & WS_CHILD) + ScreenToClient(&point); + + SetWindowPos(NULL, point.x, point.y, rect.width(), rect.height(), swp_flags); + render_widget_host_->WasResized(); + EnsureTooltip(); +} + +gfx::NativeView RenderWidgetHostViewWin::GetNativeView() const { + return m_hWnd; +} + +gfx::NativeViewId RenderWidgetHostViewWin::GetNativeViewId() const { + return reinterpret_cast<gfx::NativeViewId>(m_hWnd); +} + +void RenderWidgetHostViewWin::MovePluginWindows( + const std::vector<WebPluginGeometry>& plugin_window_moves) { + if (plugin_window_moves.empty()) + return; + + bool oop_plugins = + !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) && + !CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessPlugins); + + HDWP defer_window_pos_info = + ::BeginDeferWindowPos(static_cast<int>(plugin_window_moves.size())); + + if (!defer_window_pos_info) { + NOTREACHED(); + return; + } + + for (size_t i = 0; i < plugin_window_moves.size(); ++i) { + unsigned long flags = 0; + const WebPluginGeometry& move = plugin_window_moves[i]; + HWND window = move.window; + + // As the plugin parent window which lives on the browser UI thread is + // destroyed asynchronously, it is possible that we have a stale window + // sent in by the renderer for moving around. + // Note: get the parent before checking if the window is valid, to avoid a + // race condition where the window is destroyed after the check but before + // the GetParent call. + HWND parent = ::GetParent(window); + if (!::IsWindow(window)) + continue; + + if (oop_plugins) { + if (parent == m_hWnd) { + // The plugin window is a direct child of this window, add an + // intermediate window that lives on this thread to speed up scrolling. + // Note this only works with out of process plugins since we depend on + // PluginProcessHost to destroy the intermediate HWNDs. + parent = ReparentWindow(window); + ::ShowWindow(window, SW_SHOW); // Window was created hidden. + } else if (::GetParent(parent) != m_hWnd) { + // The renderer should only be trying to move windows that are children + // of its render widget window. However, this may happen as a result of + // a race condition, so we ignore it and not kill the plugin process. + continue; + } + + // We move the intermediate parent window which doesn't result in cross- + // process synchronous Windows messages. + window = parent; + } + + if (move.visible) + flags |= SWP_SHOWWINDOW; + else + flags |= SWP_HIDEWINDOW; + + if (move.rects_valid) { + HRGN hrgn = ::CreateRectRgn(move.clip_rect.x(), + move.clip_rect.y(), + move.clip_rect.right(), + move.clip_rect.bottom()); + gfx::SubtractRectanglesFromRegion(hrgn, move.cutout_rects); + + // Note: System will own the hrgn after we call SetWindowRgn, + // so we don't need to call DeleteObject(hrgn) + ::SetWindowRgn(window, hrgn, !move.clip_rect.IsEmpty()); + } else { + flags |= SWP_NOMOVE; + flags |= SWP_NOSIZE; + } + + defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info, + window, NULL, + move.window_rect.x(), + move.window_rect.y(), + move.window_rect.width(), + move.window_rect.height(), flags); + if (!defer_window_pos_info) { + DCHECK(false) << "DeferWindowPos failed, so all plugin moves ignored."; + return; + } + } + + ::EndDeferWindowPos(defer_window_pos_info); +} + +HWND RenderWidgetHostViewWin::ReparentWindow(HWND window) { + static ATOM window_class = 0; + if (!window_class) { + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_DBLCLKS; + wcex.lpfnWndProc = base::win::WrappedWindowProc<PluginWrapperWindowProc>; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetModuleHandle(NULL); + wcex.hIcon = 0; + wcex.hCursor = 0; + wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1); + wcex.lpszMenuName = 0; + wcex.lpszClassName = webkit::npapi::kWrapperNativeWindowClassName; + wcex.hIconSm = 0; + window_class = RegisterClassEx(&wcex); + } + DCHECK(window_class); + + HWND orig_parent = ::GetParent(window); + HWND parent = CreateWindowEx( + WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, + MAKEINTATOM(window_class), 0, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + 0, 0, 0, 0, orig_parent, 0, GetModuleHandle(NULL), 0); + ui::CheckWindowCreated(parent); + // If UIPI is enabled we need to add message filters for parents with + // children that cross process boundaries. + if (::GetPropW(orig_parent, webkit::npapi::kNativeWindowClassFilterProp)) { + // Process-wide message filters required on Vista must be added to: + // chrome_content_client.cc ChromeContentClient::SandboxPlugin + if (!g_ChangeWindowMessageFilterEx) { + g_ChangeWindowMessageFilterEx = + reinterpret_cast<ChangeWindowMessageFilterExFunction>( + ::GetProcAddress(::GetModuleHandle(L"user32.dll"), + "ChangeWindowMessageFilterEx")); + } + // Process-wide message filters required on Vista must be added to: + // chrome_content_client.cc ChromeContentClient::SandboxPlugin + g_ChangeWindowMessageFilterEx(parent, WM_MOUSEWHEEL, MSGFLT_ALLOW, NULL); + g_ChangeWindowMessageFilterEx(parent, WM_GESTURE, MSGFLT_ALLOW, NULL); + ::SetPropW(orig_parent, webkit::npapi::kNativeWindowClassFilterProp, NULL); + } + ::SetParent(window, parent); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + new NotifyPluginProcessHostTask(window, parent)); + return parent; +} + +static BOOL CALLBACK AddChildWindowToVector(HWND hwnd, LPARAM lparam) { + std::vector<HWND>* vector = reinterpret_cast<std::vector<HWND>*>(lparam); + vector->push_back(hwnd); + return TRUE; +} + +void RenderWidgetHostViewWin::CleanupCompositorWindow() { + if (!compositor_host_window_) + return; + + // Hide the compositor and parent it to the desktop rather than destroying + // it immediately. The GPU process has a grace period to stop accessing the + // window. TODO(apatrick): the GPU process should acknowledge that it has + // finished with the window handle and the browser process should destroy it + // at that point. + ::ShowWindow(compositor_host_window_, SW_HIDE); + ::SetParent(compositor_host_window_, NULL); + + BrowserThread::PostDelayedTask( + BrowserThread::UI, + FROM_HERE, + NewRunnableFunction(::DestroyWindow, compositor_host_window_), + kDestroyCompositorHostWindowDelay); + + compositor_host_window_ = NULL; +} + +bool RenderWidgetHostViewWin::IsActivatable() const { + // Popups should not be activated. + return popup_type_ == WebKit::WebPopupTypeNone; +} + +void RenderWidgetHostViewWin::Focus() { + if (IsWindow()) + SetFocus(); +} + +void RenderWidgetHostViewWin::Blur() { + views::Widget* widget = views::Widget::GetTopLevelWidgetForNativeView(m_hWnd); + if (widget) { + views::FocusManager* focus_manager = widget->GetFocusManager(); + // We don't have a FocusManager if we are hidden. + if (focus_manager) + focus_manager->ClearFocus(); + } +} + +bool RenderWidgetHostViewWin::HasFocus() { + return ::GetFocus() == m_hWnd; +} + +void RenderWidgetHostViewWin::Show() { + if (!is_fullscreen_) { + DCHECK(parent_hwnd_); + DCHECK(parent_hwnd_ != GetDesktopWindow()); + SetParent(parent_hwnd_); + } + ShowWindow(SW_SHOW); + + DidBecomeSelected(); +} + +void RenderWidgetHostViewWin::Hide() { + if (!is_fullscreen_ && GetParent() == GetDesktopWindow()) { + LOG(WARNING) << "Hide() called twice in a row: " << this << ":" << + parent_hwnd_ << ":" << GetParent(); + return; + } + + if (::GetFocus() == m_hWnd) + ::SetFocus(NULL); + ShowWindow(SW_HIDE); + + if (!is_fullscreen_) { + // Cache the old parent, then orphan the window so we stop receiving + // messages. + parent_hwnd_ = GetParent(); + SetParent(NULL); + } + + WasHidden(); +} + +bool RenderWidgetHostViewWin::IsShowing() { + return !!IsWindowVisible(); +} + +gfx::Rect RenderWidgetHostViewWin::GetViewBounds() const { + CRect window_rect; + GetWindowRect(&window_rect); + return gfx::Rect(window_rect); +} + +void RenderWidgetHostViewWin::UpdateCursor(const WebCursor& cursor) { + current_cursor_ = cursor; + UpdateCursorIfOverSelf(); +} + +void RenderWidgetHostViewWin::UpdateCursorIfOverSelf() { + static HCURSOR kCursorArrow = LoadCursor(NULL, IDC_ARROW); + static HCURSOR kCursorAppStarting = LoadCursor(NULL, IDC_APPSTARTING); + static HINSTANCE module_handle = GetModuleHandle( + content::GetContentClient()->browser()->GetResourceDllName()); + + // If the mouse is over our HWND, then update the cursor state immediately. + CPoint pt; + GetCursorPos(&pt); + if (WindowFromPoint(pt) == m_hWnd) { + BOOL result = ::ScreenToClient(m_hWnd, &pt); + DCHECK(result); + // We cannot pass in NULL as the module handle as this would only work for + // standard win32 cursors. We can also receive cursor types which are + // defined as webkit resources. We need to specify the module handle of + // chrome.dll while loading these cursors. + HCURSOR display_cursor = current_cursor_.GetCursor(module_handle); + + // If a page is in the loading state, we want to show the Arrow+Hourglass + // cursor only when the current cursor is the ARROW cursor. In all other + // cases we should continue to display the current cursor. + if (is_loading_ && display_cursor == kCursorArrow) + display_cursor = kCursorAppStarting; + + SetCursor(display_cursor); + } +} + +void RenderWidgetHostViewWin::SetIsLoading(bool is_loading) { + is_loading_ = is_loading; + UpdateCursorIfOverSelf(); +} + +void RenderWidgetHostViewWin::ImeUpdateTextInputState( + ui::TextInputType type, + bool can_compose_inline, + const gfx::Rect& caret_rect) { + // TODO(kinaba): currently, can_compose_inline is ignored and always treated + // as true. We need to support "can_compose_inline=false" for PPAPI plugins + // that may want to avoid drawing composition-text by themselves and pass + // the responsibility to the browser. + bool is_enabled = (type != ui::TEXT_INPUT_TYPE_NONE && + type != ui::TEXT_INPUT_TYPE_PASSWORD); + if (text_input_type_ != type) { + text_input_type_ = type; + if (is_enabled) + ime_input_.EnableIME(m_hWnd); + else + ime_input_.DisableIME(m_hWnd); + } + + // Only update caret position if the input method is enabled. + if (is_enabled) + ime_input_.UpdateCaretRect(m_hWnd, caret_rect); +} + +void RenderWidgetHostViewWin::ImeCancelComposition() { + ime_input_.CancelIME(m_hWnd); +} + +BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lparam) { + if (!webkit::npapi::WebPluginDelegateImpl::IsPluginDelegateWindow(hwnd)) + return TRUE; + + gfx::Rect* rect = reinterpret_cast<gfx::Rect*>(lparam); + static UINT msg = RegisterWindowMessage(webkit::npapi::kPaintMessageName); + WPARAM wparam = rect->x() << 16 | rect->y(); + lparam = rect->width() << 16 | rect->height(); + + // SendMessage gets the message across much quicker than PostMessage, since it + // doesn't get queued. When the plugin thread calls PeekMessage or other + // Win32 APIs, sent messages are dispatched automatically. + SendNotifyMessage(hwnd, msg, wparam, lparam); + + return TRUE; +} + +void RenderWidgetHostViewWin::Redraw() { + RECT damage_bounds; + GetUpdateRect(&damage_bounds, FALSE); + + base::win::ScopedGDIObject<HRGN> damage_region(CreateRectRgn(0, 0, 0, 0)); + GetUpdateRgn(damage_region, FALSE); + + // Paint the invalid region synchronously. Our caller will not paint again + // until we return, so by painting to the screen here, we ensure effective + // rate-limiting of backing store updates. This helps a lot on pages that + // have animations or fairly expensive layout (e.g., google maps). + // + // We paint this window synchronously, however child windows (i.e. plugins) + // are painted asynchronously. By avoiding synchronous cross-process window + // message dispatching we allow scrolling to be smooth, and also avoid the + // browser process locking up if the plugin process is hung. + // + RedrawWindow(NULL, damage_region, RDW_UPDATENOW | RDW_NOCHILDREN); + + // Send the invalid rect in screen coordinates. + gfx::Rect screen_rect = GetViewBounds(); + gfx::Rect invalid_screen_rect(damage_bounds); + invalid_screen_rect.Offset(screen_rect.x(), screen_rect.y()); + + LPARAM lparam = reinterpret_cast<LPARAM>(&invalid_screen_rect); + EnumChildWindows(m_hWnd, EnumChildProc, lparam); +} + +void RenderWidgetHostViewWin::DidUpdateBackingStore( + const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy, + const std::vector<gfx::Rect>& copy_rects) { + if (is_hidden_) + return; + + // Schedule invalidations first so that the ScrollWindowEx call is closer to + // Redraw. That minimizes chances of "flicker" resulting if the screen + // refreshes before we have a chance to paint the exposed area. Somewhat + // surprisingly, this ordering matters. + + for (size_t i = 0; i < copy_rects.size(); ++i) + InvalidateRect(©_rects[i].ToRECT(), false); + + if (!scroll_rect.IsEmpty()) { + RECT clip_rect = scroll_rect.ToRECT(); + ScrollWindowEx(scroll_dx, scroll_dy, NULL, &clip_rect, NULL, NULL, + SW_INVALIDATE); + } + + if (!about_to_validate_and_paint_) + Redraw(); +} + +void RenderWidgetHostViewWin::RenderViewGone(base::TerminationStatus status, + int error_code) { + // TODO(darin): keep this around, and draw sad-tab into it. + UpdateCursorIfOverSelf(); + being_destroyed_ = true; + CleanupCompositorWindow(); + DestroyWindow(); +} + +void RenderWidgetHostViewWin::WillWmDestroy() { + CleanupCompositorWindow(); +} + +void RenderWidgetHostViewWin::Destroy() { + // We've been told to destroy. + // By clearing close_on_deactivate_, we prevent further deactivations + // (caused by windows messages resulting from the DestroyWindow) from + // triggering further destructions. The deletion of this is handled by + // OnFinalMessage(); + close_on_deactivate_ = false; + render_widget_host_ = NULL; + being_destroyed_ = true; + CleanupCompositorWindow(); + DestroyWindow(); +} + +void RenderWidgetHostViewWin::SetTooltipText(const std::wstring& tooltip_text) { + // Clamp the tooltip length to kMaxTooltipLength so that we don't + // accidentally DOS the user with a mega tooltip (since Windows doesn't seem + // to do this itself). + const std::wstring& new_tooltip_text = + ui::TruncateString(tooltip_text, kMaxTooltipLength); + + if (new_tooltip_text != tooltip_text_) { + tooltip_text_ = new_tooltip_text; + + // Need to check if the tooltip is already showing so that we don't + // immediately show the tooltip with no delay when we move the mouse from + // a region with no tooltip to a region with a tooltip. + if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) { + ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + ::SendMessage(tooltip_hwnd_, TTM_POPUP, 0, 0); + } + } else { + // Make sure the tooltip gets closed after TTN_POP gets sent. For some + // reason this doesn't happen automatically, so moving the mouse around + // within the same link/image/etc doesn't cause the tooltip to re-appear. + if (!tooltip_showing_) { + if (::IsWindow(tooltip_hwnd_)) + ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + } + } +} + +BackingStore* RenderWidgetHostViewWin::AllocBackingStore( + const gfx::Size& size) { + return new BackingStoreWin(render_widget_host_, size); +} + +void RenderWidgetHostViewWin::SetBackground(const SkBitmap& background) { + RenderWidgetHostView::SetBackground(background); + Send(new ViewMsg_SetBackground(render_widget_host_->routing_id(), + background)); +} + +void RenderWidgetHostViewWin::SetVisuallyDeemphasized(const SkColor* color, + bool animate) { + // |animate| is not yet implemented, and currently isn't used. + CHECK(!animate); + + SkColor overlay_color = color ? *color : 0; + if (overlay_color_ == overlay_color) + return; + overlay_color_ = overlay_color; + + InvalidateRect(NULL, FALSE); +} + +void RenderWidgetHostViewWin::UnhandledWheelEvent( + const WebKit::WebMouseWheelEvent& event) { +} + +void RenderWidgetHostViewWin::SetHasHorizontalScrollbar( + bool has_horizontal_scrollbar) { +} + +void RenderWidgetHostViewWin::SetScrollOffsetPinning( + bool is_pinned_to_left, bool is_pinned_to_right) { +} + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostViewWin, private: + +LRESULT RenderWidgetHostViewWin::OnCreate(CREATESTRUCT* create_struct) { + // Call the WM_INPUTLANGCHANGE message handler to initialize the input locale + // of a browser process. + OnInputLangChange(0, 0); + // Marks that window as supporting mouse-wheel messages rerouting so it is + // scrolled when under the mouse pointer even if inactive. + props_.push_back(views::SetWindowSupportsRerouteMouseWheel(m_hWnd)); + props_.push_back(new ViewProp(m_hWnd, kRenderWidgetHostViewKey, + static_cast<RenderWidgetHostView*>(this))); + + return 0; +} + +void RenderWidgetHostViewWin::OnActivate(UINT action, BOOL minimized, + HWND window) { + // If the container is a popup, clicking elsewhere on screen should close the + // popup. + if (close_on_deactivate_ && action == WA_INACTIVE) { + // Send a windows message so that any derived classes + // will get a change to override the default handling + SendMessage(WM_CANCELMODE); + } +} + +void RenderWidgetHostViewWin::OnDestroy() { + // When a tab is closed all its child plugin windows are destroyed + // automatically. This happens before plugins get any notification that its + // instances are tearing down. + // + // Plugins like Quicktime assume that their windows will remain valid as long + // as they have plugin instances active. Quicktime crashes in this case + // because its windowing code cleans up an internal data structure that the + // handler for NPP_DestroyStream relies on. + // + // The fix is to detach plugin windows from web contents when it is going + // away. This will prevent the plugin windows from getting destroyed + // automatically. The detached plugin windows will get cleaned up in proper + // sequence as part of the usual cleanup when the plugin instance goes away. + EnumChildWindows(m_hWnd, DetachPluginWindowsCallback, NULL); + + props_.reset(); + + CleanupCompositorWindow(); + + ResetTooltip(); + TrackMouseLeave(false); +} + +void RenderWidgetHostViewWin::OnPaint(HDC unused_dc) { + if (!render_widget_host_) + return; + + DCHECK(render_widget_host_->process()->HasConnection()); + + // If the GPU process is rendering directly into the View, compositing is + // already triggered by damage to compositor_host_window_, so all we need to + // do here is clear borders during resize. + if (render_widget_host_->is_accelerated_compositing_active()) { + // We initialize paint_dc here so that BeginPaint()/EndPaint() + // get called to validate the region. + CPaintDC paint_dc(m_hWnd); + RECT host_rect, child_rect; + GetClientRect(&host_rect); + if (::GetClientRect(compositor_host_window_, &child_rect) && + (child_rect.right < host_rect.right || + child_rect.bottom < host_rect.bottom)) { + paint_dc.FillRect(&host_rect, + reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH))); + } + return; + } + + about_to_validate_and_paint_ = true; + BackingStoreWin* backing_store = static_cast<BackingStoreWin*>( + render_widget_host_->GetBackingStore(true)); + + // We initialize |paint_dc| (and thus call BeginPaint()) after calling + // GetBackingStore(), so that if it updates the invalid rect we'll catch the + // changes and repaint them. + about_to_validate_and_paint_ = false; + + // Grab the region to paint before creation of paint_dc since it clears the + // damage region. + base::win::ScopedGDIObject<HRGN> damage_region(CreateRectRgn(0, 0, 0, 0)); + GetUpdateRgn(damage_region, FALSE); + + if (hide_compositor_window_at_next_paint_) { + ::ShowWindow(compositor_host_window_, SW_HIDE); + hide_compositor_window_at_next_paint_ = false; + } + + CPaintDC paint_dc(m_hWnd); + + gfx::Rect damaged_rect(paint_dc.m_ps.rcPaint); + if (damaged_rect.IsEmpty()) + return; + + if (backing_store) { + gfx::Rect bitmap_rect(gfx::Point(), backing_store->size()); + + bool manage_colors = BackingStoreWin::ColorManagementEnabled(); + if (manage_colors) + SetICMMode(paint_dc.m_hDC, ICM_ON); + + // Blit only the damaged regions from the backing store. + DWORD data_size = GetRegionData(damage_region, 0, NULL); + scoped_array<char> region_data_buf(new char[data_size]); + RGNDATA* region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get()); + GetRegionData(damage_region, data_size, region_data); + + RECT* region_rects = reinterpret_cast<RECT*>(region_data->Buffer); + for (DWORD i = 0; i < region_data->rdh.nCount; ++i) { + gfx::Rect paint_rect = bitmap_rect.Intersect(gfx::Rect(region_rects[i])); + if (!paint_rect.IsEmpty()) { + if (SkColorGetA(overlay_color_) > 0) { + DrawDeemphasized(overlay_color_, + paint_rect, + backing_store->hdc(), + paint_dc.m_hDC); + } else { + BitBlt(paint_dc.m_hDC, + paint_rect.x(), + paint_rect.y(), + paint_rect.width(), + paint_rect.height(), + backing_store->hdc(), + paint_rect.x(), + paint_rect.y(), + SRCCOPY); + } + } + } + + if (manage_colors) + SetICMMode(paint_dc.m_hDC, ICM_OFF); + + // Fill the remaining portion of the damaged_rect with the background + if (damaged_rect.right() > bitmap_rect.right()) { + RECT r; + r.left = std::max(bitmap_rect.right(), damaged_rect.x()); + r.right = damaged_rect.right(); + r.top = damaged_rect.y(); + r.bottom = std::min(bitmap_rect.bottom(), damaged_rect.bottom()); + DrawBackground(r, &paint_dc); + } + if (damaged_rect.bottom() > bitmap_rect.bottom()) { + RECT r; + r.left = damaged_rect.x(); + r.right = damaged_rect.right(); + r.top = std::max(bitmap_rect.bottom(), damaged_rect.y()); + r.bottom = damaged_rect.bottom(); + DrawBackground(r, &paint_dc); + } + if (!whiteout_start_time_.is_null()) { + TimeDelta whiteout_duration = TimeTicks::Now() - whiteout_start_time_; + UMA_HISTOGRAM_TIMES("MPArch.RWHH_WhiteoutDuration", whiteout_duration); + + // Reset the start time to 0 so that we start recording again the next + // time the backing store is NULL... + whiteout_start_time_ = TimeTicks(); + } + if (!tab_switch_paint_time_.is_null()) { + TimeDelta tab_switch_paint_duration = TimeTicks::Now() - + tab_switch_paint_time_; + UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", + tab_switch_paint_duration); + // Reset tab_switch_paint_time_ to 0 so future tab selections are + // recorded. + tab_switch_paint_time_ = TimeTicks(); + } + } else { + DrawBackground(paint_dc.m_ps.rcPaint, &paint_dc); + if (whiteout_start_time_.is_null()) + whiteout_start_time_ = TimeTicks::Now(); + } +} + +void RenderWidgetHostViewWin::DrawBackground(const RECT& dirty_rect, + CPaintDC* dc) { + if (!background_.empty()) { + gfx::CanvasSkia canvas(dirty_rect.right - dirty_rect.left, + dirty_rect.bottom - dirty_rect.top, + true); // opaque + canvas.TranslateInt(-dirty_rect.left, -dirty_rect.top); + + const RECT& dc_rect = dc->m_ps.rcPaint; + canvas.TileImageInt(background_, 0, 0, + dc_rect.right - dc_rect.left, + dc_rect.bottom - dc_rect.top); + + skia::DrawToNativeContext(&canvas, *dc, dirty_rect.left, dirty_rect.top, + NULL); + } else { + HBRUSH white_brush = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)); + dc->FillRect(&dirty_rect, white_brush); + } +} + +void RenderWidgetHostViewWin::OnNCPaint(HRGN update_region) { + // Do nothing. This suppresses the resize corner that Windows would + // otherwise draw for us. +} + +LRESULT RenderWidgetHostViewWin::OnEraseBkgnd(HDC dc) { + return 1; +} + +LRESULT RenderWidgetHostViewWin::OnSetCursor(HWND window, UINT hittest_code, + UINT mouse_message_id) { + UpdateCursorIfOverSelf(); + return 0; +} + +void RenderWidgetHostViewWin::OnSetFocus(HWND window) { + views::FocusManager::GetWidgetFocusManager()->OnWidgetFocusEvent(window, + m_hWnd); + if (browser_accessibility_manager_.get()) + browser_accessibility_manager_->GotFocus(); + if (render_widget_host_) + render_widget_host_->GotFocus(); +} + +void RenderWidgetHostViewWin::OnKillFocus(HWND window) { + views::FocusManager::GetWidgetFocusManager()->OnWidgetFocusEvent(m_hWnd, + window); + + if (render_widget_host_) + render_widget_host_->Blur(); +} + +void RenderWidgetHostViewWin::OnCaptureChanged(HWND window) { + if (render_widget_host_) + render_widget_host_->LostCapture(); +} + +void RenderWidgetHostViewWin::OnCancelMode() { + if (render_widget_host_) + render_widget_host_->LostCapture(); + + if ((is_fullscreen_ || close_on_deactivate_) && + shutdown_factory_.empty()) { + // Dismiss popups and menus. We do this asynchronously to avoid changing + // activation within this callstack, which may interfere with another window + // being activated. We can synchronously hide the window, but we need to + // not change activation while doing so. + SetWindowPos(NULL, 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); + MessageLoop::current()->PostTask(FROM_HERE, + shutdown_factory_.NewRunnableMethod( + &RenderWidgetHostViewWin::ShutdownHost)); + } +} + +void RenderWidgetHostViewWin::OnInputLangChange(DWORD character_set, + HKL input_language_id) { + // Send the given Locale ID to the ImeInput object and retrieves whether + // or not the current input context has IMEs. + // If the current input context has IMEs, a browser process has to send a + // request to a renderer process that it needs status messages about + // the focused edit control from the renderer process. + // On the other hand, if the current input context does not have IMEs, the + // browser process also has to send a request to the renderer process that + // it does not need the status messages any longer. + // To minimize the number of this notification request, we should check if + // the browser process is actually retrieving the status messages (this + // state is stored in ime_notification_) and send a request only if the + // browser process has to update this status, its details are listed below: + // * If a browser process is not retrieving the status messages, + // (i.e. ime_notification_ == false), + // send this request only if the input context does have IMEs, + // (i.e. ime_status == true); + // When it successfully sends the request, toggle its notification status, + // (i.e.ime_notification_ = !ime_notification_ = true). + // * If a browser process is retrieving the status messages + // (i.e. ime_notification_ == true), + // send this request only if the input context does not have IMEs, + // (i.e. ime_status == false). + // When it successfully sends the request, toggle its notification status, + // (i.e.ime_notification_ = !ime_notification_ = false). + // To analyze the above actions, we can optimize them into the ones + // listed below: + // 1 Sending a request only if ime_status_ != ime_notification_, and; + // 2 Copying ime_status to ime_notification_ if it sends the request + // successfully (because Action 1 shows ime_status = !ime_notification_.) + bool ime_status = ime_input_.SetInputLanguage(); + if (ime_status != ime_notification_) { + if (render_widget_host_) { + render_widget_host_->SetInputMethodActive(ime_status); + ime_notification_ = ime_status; + } + } +} + +void RenderWidgetHostViewWin::OnThemeChanged() { + if (!render_widget_host_) + return; + render_widget_host_->Send(new ViewMsg_ThemeChanged( + render_widget_host_->routing_id())); +} + +LRESULT RenderWidgetHostViewWin::OnNotify(int w_param, NMHDR* header) { + if (tooltip_hwnd_ == NULL) + return 0; + + switch (header->code) { + case TTN_GETDISPINFO: { + NMTTDISPINFOW* tooltip_info = reinterpret_cast<NMTTDISPINFOW*>(header); + tooltip_info->szText[0] = L'\0'; + tooltip_info->lpszText = const_cast<wchar_t*>(tooltip_text_.c_str()); + ::SendMessage( + tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, kTooltipMaxWidthPixels); + SetMsgHandled(TRUE); + break; + } + case TTN_POP: + tooltip_showing_ = false; + SetMsgHandled(TRUE); + break; + case TTN_SHOW: + tooltip_showing_ = true; + SetMsgHandled(TRUE); + break; + } + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnImeSetContext( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + if (!render_widget_host_) + return 0; + + // We need status messages about the focused input control from a + // renderer process when: + // * the current input context has IMEs, and; + // * an application is activated. + // This seems to tell we should also check if the current input context has + // IMEs before sending a request, however, this WM_IME_SETCONTEXT is + // fortunately sent to an application only while the input context has IMEs. + // Therefore, we just start/stop status messages according to the activation + // status of this application without checks. + bool activated = (wparam == TRUE); + if (render_widget_host_) { + render_widget_host_->SetInputMethodActive(activated); + ime_notification_ = activated; + } + + if (ime_notification_) + ime_input_.CreateImeWindow(m_hWnd); + + ime_input_.CleanupComposition(m_hWnd); + return ime_input_.SetImeWindowStyle( + m_hWnd, message, wparam, lparam, &handled); +} + +LRESULT RenderWidgetHostViewWin::OnImeStartComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + if (!render_widget_host_) + return 0; + + // Reset the composition status and create IME windows. + ime_input_.CreateImeWindow(m_hWnd); + ime_input_.ResetComposition(m_hWnd); + // We have to prevent WTL from calling ::DefWindowProc() because the function + // calls ::ImmSetCompositionWindow() and ::ImmSetCandidateWindow() to + // over-write the position of IME windows. + handled = TRUE; + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnImeComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + if (!render_widget_host_) + return 0; + + // At first, update the position of the IME window. + ime_input_.UpdateImeWindow(m_hWnd); + + // ui::CompositionUnderline should be identical to + // WebKit::WebCompositionUnderline, so that we can do reinterpret_cast safely. + COMPILE_ASSERT(sizeof(ui::CompositionUnderline) == + sizeof(WebKit::WebCompositionUnderline), + ui_CompositionUnderline__WebKit_WebCompositionUnderline_diff); + + // Retrieve the result string and its attributes of the ongoing composition + // and send it to a renderer process. + ui::CompositionText composition; + if (ime_input_.GetResult(m_hWnd, lparam, &composition.text)) { + render_widget_host_->ImeConfirmComposition(composition.text); + ime_input_.ResetComposition(m_hWnd); + // Fall though and try reading the composition string. + // Japanese IMEs send a message containing both GCS_RESULTSTR and + // GCS_COMPSTR, which means an ongoing composition has been finished + // by the start of another composition. + } + // Retrieve the composition string and its attributes of the ongoing + // composition and send it to a renderer process. + if (ime_input_.GetComposition(m_hWnd, lparam, &composition)) { + // TODO(suzhe): due to a bug of webkit, we can't use selection range with + // composition string. See: https://bugs.webkit.org/show_bug.cgi?id=37788 + composition.selection = ui::Range(composition.selection.end()); + + // TODO(suzhe): convert both renderer_host and renderer to use + // ui::CompositionText. + const std::vector<WebKit::WebCompositionUnderline>& underlines = + reinterpret_cast<const std::vector<WebKit::WebCompositionUnderline>&>( + composition.underlines); + render_widget_host_->ImeSetComposition( + composition.text, underlines, + composition.selection.start(), composition.selection.end()); + } + // We have to prevent WTL from calling ::DefWindowProc() because we do not + // want for the IMM (Input Method Manager) to send WM_IME_CHAR messages. + handled = TRUE; + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnImeEndComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + if (!render_widget_host_) + return 0; + + if (ime_input_.is_composing()) { + // A composition has been ended while there is an ongoing composition, + // i.e. the ongoing composition has been canceled. + // We need to reset the composition status both of the ImeInput object and + // of the renderer process. + render_widget_host_->ImeCancelComposition(); + ime_input_.ResetComposition(m_hWnd); + } + ime_input_.DestroyImeWindow(m_hWnd); + // Let WTL call ::DefWindowProc() and release its resources. + handled = FALSE; + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnMouseEvent(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + handled = TRUE; + + if (::IsWindow(tooltip_hwnd_)) { + // Forward mouse events through to the tooltip window + MSG msg; + msg.hwnd = m_hWnd; + msg.message = message; + msg.wParam = wparam; + msg.lParam = lparam; + SendMessage(tooltip_hwnd_, TTM_RELAYEVENT, NULL, + reinterpret_cast<LPARAM>(&msg)); + } + + // TODO(jcampan): I am not sure if we should forward the message to the + // TabContents first in the case of popups. If we do, we would need to + // convert the click from the popup window coordinates to the TabContents' + // window coordinates. For now we don't forward the message in that case to + // address bug #907474. + // Note: GetParent() on popup windows returns the top window and not the + // parent the window was created with (the parent and the owner of the popup + // is the first non-child view of the view that was specified to the create + // call). So the TabContents window would have to be specified to the + // RenderViewHostHWND as there is no way to retrieve it from the HWND. + + // Don't forward if the container is a popup or fullscreen widget. + if (!is_fullscreen_ && !close_on_deactivate_) { + switch (message) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + // Finish the ongoing composition whenever a mouse click happens. + // It matches IE's behavior. + ime_input_.CleanupComposition(m_hWnd); + // Fall through. + case WM_MOUSEMOVE: + case WM_MOUSELEAVE: { + // Give the TabContents first crack at the message. It may want to + // prevent forwarding to the renderer if some higher level browser + // functionality is invoked. + LPARAM parent_msg_lparam = lparam; + if (message != WM_MOUSELEAVE) { + // For the messages except WM_MOUSELEAVE, before forwarding them to + // parent window, we should adjust cursor position from client + // coordinates in current window to client coordinates in its parent + // window. + CPoint cursor_pos(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)); + ClientToScreen(&cursor_pos); + GetParent().ScreenToClient(&cursor_pos); + parent_msg_lparam = MAKELPARAM(cursor_pos.x, cursor_pos.y); + } + if (SendMessage(GetParent(), message, wparam, parent_msg_lparam) != 0) + return 1; + } + } + } + + ForwardMouseEventToRenderer(message, wparam, lparam); + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnKeyEvent(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + handled = TRUE; + + // Force fullscreen windows to close on Escape. + if (is_fullscreen_ && (message == WM_KEYDOWN || message == WM_KEYUP) && + wparam == VK_ESCAPE) { + SendMessage(WM_CANCELMODE); + return 0; + } + + // If we are a pop-up, forward tab related messages to our parent HWND, so + // that we are dismissed appropriately and so that the focus advance in our + // parent. + // TODO(jcampan): http://b/issue?id=1192881 Could be abstracted in the + // FocusManager. + if (close_on_deactivate_ && + (((message == WM_KEYDOWN || message == WM_KEYUP) && (wparam == VK_TAB)) || + (message == WM_CHAR && wparam == L'\t'))) { + DCHECK(parent_hwnd_); + // First close the pop-up. + SendMessage(WM_CANCELMODE); + // Then move the focus by forwarding the tab key to the parent. + return ::SendMessage(parent_hwnd_, message, wparam, lparam); + } + + if (!render_widget_host_) + return 0; + + // Bug 1845: we need to update the text direction when a user releases + // either a right-shift key or a right-control key after pressing both of + // them. So, we just update the text direction while a user is pressing the + // keys, and we notify the text direction when a user releases either of them. + // Bug 9718: http://crbug.com/9718 To investigate IE and notepad, this + // shortcut is enabled only on a PC having RTL keyboard layouts installed. + // We should emulate them. + if (ui::ImeInput::IsRTLKeyboardLayoutInstalled()) { + if (message == WM_KEYDOWN) { + if (wparam == VK_SHIFT) { + base::i18n::TextDirection dir; + if (ui::ImeInput::IsCtrlShiftPressed(&dir)) { + render_widget_host_->UpdateTextDirection( + dir == base::i18n::RIGHT_TO_LEFT ? + WebKit::WebTextDirectionRightToLeft : + WebKit::WebTextDirectionLeftToRight); + } + } else if (wparam != VK_CONTROL) { + // Bug 9762: http://crbug.com/9762 A user pressed a key except shift + // and control keys. + // When a user presses a key while he/she holds control and shift keys, + // we cancel sending an IPC message in NotifyTextDirection() below and + // ignore succeeding UpdateTextDirection() calls while we call + // NotifyTextDirection(). + // To cancel it, this call set a flag that prevents sending an IPC + // message in NotifyTextDirection() only if we are going to send it. + // It is harmless to call this function if we aren't going to send it. + render_widget_host_->CancelUpdateTextDirection(); + } + } else if (message == WM_KEYUP && + (wparam == VK_SHIFT || wparam == VK_CONTROL)) { + // We send an IPC message only if we need to update the text direction. + render_widget_host_->NotifyTextDirection(); + } + } + + // Special processing for enter key: When user hits enter in omnibox + // we change focus to render host after the navigation, so repeat WM_KEYDOWNs + // and WM_KEYUP are going to render host, despite being initiated in other + // window. This code filters out these messages. + bool ignore_keyboard_event = false; + if (wparam == VK_RETURN) { + if (message == WM_KEYDOWN || message == WM_SYSKEYDOWN) { + if (KF_REPEAT & HIWORD(lparam)) { + // this is a repeated key + if (!capture_enter_key_) + ignore_keyboard_event = true; + } else { + capture_enter_key_ = true; + } + } else if (message == WM_KEYUP || message == WM_SYSKEYUP) { + if (!capture_enter_key_) + ignore_keyboard_event = true; + capture_enter_key_ = false; + } else { + // Ignore all other keyboard events for the enter key if not captured. + if (!capture_enter_key_) + ignore_keyboard_event = true; + } + } + + if (render_widget_host_ && !ignore_keyboard_event) { + render_widget_host_->ForwardKeyboardEvent( + NativeWebKeyboardEvent(m_hWnd, message, wparam, lparam)); + } + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnWheelEvent(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + // Forward the mouse-wheel message to the window under the mouse if it belongs + // to us. + if (message == WM_MOUSEWHEEL && + views::RerouteMouseWheel(m_hWnd, wparam, lparam)) { + handled = TRUE; + return 0; + } + + // Workaround for Thinkpad mousewheel driver. We get mouse wheel/scroll + // messages even if we are not in the foreground. So here we check if + // we have any owned popup windows in the foreground and dismiss them. + if (m_hWnd != GetForegroundWindow()) { + HWND toplevel_hwnd = ::GetAncestor(m_hWnd, GA_ROOT); + EnumThreadWindows( + GetCurrentThreadId(), + DismissOwnedPopups, + reinterpret_cast<LPARAM>(toplevel_hwnd)); + } + + // This is a bit of a hack, but will work for now since we don't want to + // pollute this object with TabContents-specific functionality... + bool handled_by_TabContents = false; + if (!is_fullscreen_ && GetParent()) { + // Use a special reflected message to break recursion. If we send + // WM_MOUSEWHEEL, the focus manager subclass of web contents will + // route it back here. + MSG new_message = {0}; + new_message.hwnd = m_hWnd; + new_message.message = message; + new_message.wParam = wparam; + new_message.lParam = lparam; + + handled_by_TabContents = + !!::SendMessage(GetParent(), views::kReflectedMessage, 0, + reinterpret_cast<LPARAM>(&new_message)); + } + + if (!handled_by_TabContents && render_widget_host_) { + render_widget_host_->ForwardWheelEvent( + WebInputEventFactory::mouseWheelEvent(m_hWnd, message, wparam, + lparam)); + } + handled = TRUE; + return 0; +} + +LRESULT RenderWidgetHostViewWin::OnMouseActivate(UINT message, + WPARAM wparam, + LPARAM lparam, + BOOL& handled) { + if (!render_widget_host_) + return MA_NOACTIVATE; + + if (!IsActivatable()) + return MA_NOACTIVATE; + + HWND focus_window = GetFocus(); + if (!::IsWindow(focus_window) || !IsChild(focus_window)) { + // We handle WM_MOUSEACTIVATE to set focus to the underlying plugin + // child window. This is to ensure that keyboard events are received + // by the plugin. The correct way to fix this would be send over + // an event to the renderer which would then eventually send over + // a setFocus call to the plugin widget. This would ensure that + // the renderer (webkit) knows about the plugin widget receiving + // focus. + // TODO(iyengar) Do the right thing as per the above comment. + POINT cursor_pos = {0}; + ::GetCursorPos(&cursor_pos); + ::ScreenToClient(m_hWnd, &cursor_pos); + HWND child_window = ::RealChildWindowFromPoint(m_hWnd, cursor_pos); + if (::IsWindow(child_window) && child_window != m_hWnd) { + if (ui::GetClassName(child_window) == + webkit::npapi::kWrapperNativeWindowClassName) + child_window = ::GetWindow(child_window, GW_CHILD); + + ::SetFocus(child_window); + return MA_NOACTIVATE; + } + } + handled = FALSE; + render_widget_host_->OnMouseActivate(); + return MA_ACTIVATE; +} + +void RenderWidgetHostViewWin::OnAccessibilityNotifications( + const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) { + if (!browser_accessibility_manager_.get()) { + browser_accessibility_manager_.reset( + BrowserAccessibilityManager::CreateEmptyDocument( + m_hWnd, static_cast<WebAccessibility::State>(0), this)); + } + browser_accessibility_manager_->OnAccessibilityNotifications(params); +} + +void RenderWidgetHostViewWin::Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_TERMINATED); + + // Get the RenderProcessHost that posted this notification, and exit + // if it's not the one associated with this host view. + RenderProcessHost* render_process_host = + Source<RenderProcessHost>(source).ptr(); + DCHECK(render_process_host); + if (!render_widget_host_ || + render_process_host != render_widget_host_->process()) + return; + + // If it was our RenderProcessHost that posted the notification, + // clear the BrowserAccessibilityManager, because the renderer is + // dead and any accessibility information we have is now stale. + browser_accessibility_manager_.reset(NULL); +} + +static void PaintCompositorHostWindow(HWND hWnd) { + PAINTSTRUCT paint; + BeginPaint(hWnd, &paint); + + RenderWidgetHostViewWin* win = static_cast<RenderWidgetHostViewWin*>( + ui::GetWindowUserData(hWnd)); + // Trigger composite to rerender window. + if (win) + win->ScheduleComposite(); + + EndPaint(hWnd, &paint); +} + +// WndProc for the compositor host window. We use this instead of Default so +// we can drop WM_PAINT and WM_ERASEBKGD messages on the floor. +static LRESULT CALLBACK CompositorHostWindowProc(HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_ERASEBKGND: + return 0; + case WM_DESTROY: + ui::SetWindowUserData(hWnd, NULL); + return 0; + case WM_PAINT: + PaintCompositorHostWindow(hWnd); + return 0; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } +} + +void RenderWidgetHostViewWin::ScheduleComposite() { + if (render_widget_host_) + render_widget_host_->ScheduleComposite(); +} + +// Creates a HWND within the RenderWidgetHostView that will serve as a host +// for a HWND that the GPU process will create. The host window is used +// to Z-position the GPU's window relative to other plugin windows. +gfx::PluginWindowHandle RenderWidgetHostViewWin::GetCompositingSurface() { + // If the window has been created, don't recreate it a second time + if (compositor_host_window_) + return compositor_host_window_; + + static ATOM window_class = 0; + if (!window_class) { + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = 0; + wcex.lpfnWndProc = + base::win::WrappedWindowProc<CompositorHostWindowProc>; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetModuleHandle(NULL); + wcex.hIcon = 0; + wcex.hCursor = 0; + wcex.hbrBackground = NULL; + wcex.lpszMenuName = 0; + wcex.lpszClassName = L"CompositorHostWindowClass"; + wcex.hIconSm = 0; + window_class = RegisterClassEx(&wcex); + DCHECK(window_class); + } + + RECT currentRect; + GetClientRect(¤tRect); + + // Ensure window does not have zero area because D3D cannot create a zero + // area swap chain. + int width = std::max(1, + static_cast<int>(currentRect.right - currentRect.left)); + int height = std::max(1, + static_cast<int>(currentRect.bottom - currentRect.top)); + + compositor_host_window_ = CreateWindowEx( + WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, + MAKEINTATOM(window_class), 0, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_DISABLED, + 0, 0, width, height, m_hWnd, 0, GetModuleHandle(NULL), 0); + ui::CheckWindowCreated(compositor_host_window_); + + ui::SetWindowUserData(compositor_host_window_, this); + + return static_cast<gfx::PluginWindowHandle>(compositor_host_window_); +} + +void RenderWidgetHostViewWin::ShowCompositorHostWindow(bool show) { + // When we first create the compositor, we will get a show request from + // the renderer before we have gotten the create request from the GPU. In this + // case, simply ignore the show request. + if (compositor_host_window_ == NULL) + return; + + if (show) { + ::ShowWindow(compositor_host_window_, SW_SHOW); + + // Get all the child windows of this view, including the compositor window. + std::vector<HWND> all_child_windows; + ::EnumChildWindows(m_hWnd, AddChildWindowToVector, + reinterpret_cast<LPARAM>(&all_child_windows)); + + // Build a list of just the plugin window handles + std::vector<HWND> plugin_windows; + bool compositor_host_window_found = false; + for (size_t i = 0; i < all_child_windows.size(); ++i) { + if (all_child_windows[i] != compositor_host_window_) + plugin_windows.push_back(all_child_windows[i]); + else + compositor_host_window_found = true; + } + DCHECK(compositor_host_window_found); + + // Set all the plugin windows to be "after" the compositor window. + // When the compositor window is created, gets placed above plugins. + for (size_t i = 0; i < plugin_windows.size(); ++i) { + HWND next; + if (i + 1 < plugin_windows.size()) + next = plugin_windows[i+1]; + else + next = compositor_host_window_; + ::SetWindowPos(plugin_windows[i], next, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + } + } else { + hide_compositor_window_at_next_paint_ = true; + } +} + +void RenderWidgetHostViewWin::SetAccessibilityFocus(int acc_obj_id) { + if (!render_widget_host_) + return; + + render_widget_host_->Send(new ViewMsg_SetAccessibilityFocus( + render_widget_host_->routing_id(), acc_obj_id)); +} + +void RenderWidgetHostViewWin::AccessibilityDoDefaultAction(int acc_obj_id) { + if (!render_widget_host_) + return; + + render_widget_host_->Send(new ViewMsg_AccessibilityDoDefaultAction( + render_widget_host_->routing_id(), acc_obj_id)); +} + +IAccessible* RenderWidgetHostViewWin::GetIAccessible() { + if (render_widget_host_ && !render_widget_host_->renderer_accessible()) { + // Attempt to detect screen readers by sending an event with our custom id. + NotifyWinEvent(EVENT_SYSTEM_ALERT, m_hWnd, kIdCustom, CHILDID_SELF); + } + + if (!browser_accessibility_manager_.get()) { + // Return busy document tree while renderer accessibility tree loads. + WebAccessibility::State busy_state = + static_cast<WebAccessibility::State>(1 << WebAccessibility::STATE_BUSY); + browser_accessibility_manager_.reset( + BrowserAccessibilityManager::CreateEmptyDocument( + m_hWnd, busy_state, this)); + } + + return browser_accessibility_manager_->GetRoot()->toBrowserAccessibilityWin(); +} + +LRESULT RenderWidgetHostViewWin::OnGetObject(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + if (kIdCustom == lparam) { + // An MSAA client requestes our custom id. Assume that we have detected an + // active windows screen reader. + BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected(); + render_widget_host_->EnableRendererAccessibility(); + + // Return with failure. + return static_cast<LRESULT>(0L); + } + + if (lparam != OBJID_CLIENT) { + handled = false; + return static_cast<LRESULT>(0L); + } + + IAccessible* iaccessible = GetIAccessible(); + if (iaccessible) + return LresultFromObject(IID_IAccessible, wparam, iaccessible); + + handled = false; + return static_cast<LRESULT>(0L); +} + +LRESULT RenderWidgetHostViewWin::OnParentNotify(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + handled = FALSE; + + if (!render_widget_host_) + return 0; + + switch (LOWORD(wparam)) { + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + render_widget_host_->StartUserGesture(); + break; + default: + break; + } + return 0; +} + +void RenderWidgetHostViewWin::OnFinalMessage(HWND window) { + // When the render widget host is being destroyed, it ends up calling + // Destroy() which NULLs render_widget_host_. + // Note: the following bug http://crbug.com/24248 seems to report that + // OnFinalMessage is called with a deleted |render_widget_host_|. It is not + // clear how this could happen, hence the NULLing of render_widget_host_ + // above. + if (!render_widget_host_ && !being_destroyed_) { + // If you hit this NOTREACHED, please add a comment to report it on + // http://crbug.com/24248, including what you did when it happened and if + // you can repro. + NOTREACHED(); + } + if (render_widget_host_) + render_widget_host_->ViewDestroyed(); + delete this; +} + +void RenderWidgetHostViewWin::TrackMouseLeave(bool track) { + if (track == track_mouse_leave_) + return; + track_mouse_leave_ = track; + + DCHECK(m_hWnd); + + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + if (!track_mouse_leave_) + tme.dwFlags |= TME_CANCEL; + tme.hwndTrack = m_hWnd; + + TrackMouseEvent(&tme); +} + +bool RenderWidgetHostViewWin::Send(IPC::Message* message) { + if (!render_widget_host_) + return false; + return render_widget_host_->Send(message); +} + +void RenderWidgetHostViewWin::EnsureTooltip() { + UINT message = TTM_NEWTOOLRECT; + + TOOLINFO ti; + ti.cbSize = sizeof(ti); + ti.hwnd = m_hWnd; + ti.uId = 0; + if (!::IsWindow(tooltip_hwnd_)) { + message = TTM_ADDTOOL; + tooltip_hwnd_ = CreateWindowEx( + WS_EX_TRANSPARENT | l10n_util::GetExtendedTooltipStyles(), + TOOLTIPS_CLASS, NULL, TTS_NOPREFIX, 0, 0, 0, 0, m_hWnd, NULL, + NULL, NULL); + if (!tooltip_hwnd_) { + // Tooltip creation can inexplicably fail. See bug 82913 for details. + LOG_GETLASTERROR(WARNING) << + "Tooltip creation failed, tooltips won't work"; + return; + } + ti.uFlags = TTF_TRANSPARENT; + ti.lpszText = LPSTR_TEXTCALLBACK; + + // Ensure web content tooltips are displayed for at least this amount of + // time, to give users a chance to read longer messages. + const int kMinimumAutopopDurationMs = 10 * 1000; + int autopop_duration_ms = + SendMessage(tooltip_hwnd_, TTM_GETDELAYTIME, TTDT_AUTOPOP, NULL); + if (autopop_duration_ms < kMinimumAutopopDurationMs) { + SendMessage(tooltip_hwnd_, TTM_SETDELAYTIME, TTDT_AUTOPOP, + kMinimumAutopopDurationMs); + } + } + + CRect cr; + GetClientRect(&ti.rect); + SendMessage(tooltip_hwnd_, message, NULL, reinterpret_cast<LPARAM>(&ti)); +} + +void RenderWidgetHostViewWin::ResetTooltip() { + if (::IsWindow(tooltip_hwnd_)) + ::DestroyWindow(tooltip_hwnd_); + tooltip_hwnd_ = NULL; +} + +void RenderWidgetHostViewWin::ForwardMouseEventToRenderer(UINT message, + WPARAM wparam, + LPARAM lparam) { + if (!render_widget_host_) + return; + + WebMouseEvent event( + WebInputEventFactory::mouseEvent(m_hWnd, message, wparam, lparam)); + + // Send the event to the renderer before changing mouse capture, so that the + // capturelost event arrives after mouseup. + render_widget_host_->ForwardMouseEvent(event); + + switch (event.type) { + case WebInputEvent::MouseMove: + TrackMouseLeave(true); + break; + case WebInputEvent::MouseLeave: + TrackMouseLeave(false); + break; + case WebInputEvent::MouseDown: + SetCapture(); + break; + case WebInputEvent::MouseUp: + if (GetCapture() == m_hWnd) + ReleaseCapture(); + break; + } + + if (IsActivatable() && event.type == WebInputEvent::MouseDown) { + // This is a temporary workaround for bug 765011 to get focus when the + // mouse is clicked. This happens after the mouse down event is sent to + // the renderer because normally Windows does a WM_SETFOCUS after + // WM_LBUTTONDOWN. + SetFocus(); + } +} + +void RenderWidgetHostViewWin::ShutdownHost() { + shutdown_factory_.RevokeAll(); + if (render_widget_host_) + render_widget_host_->Shutdown(); + // Do not touch any members at this point, |this| has been deleted. +} + +void RenderWidgetHostViewWin::DoPopupOrFullscreenInit(HWND parent_hwnd, + const gfx::Rect& pos, + DWORD ex_style) { + parent_hwnd_ = parent_hwnd; + Create(parent_hwnd_, NULL, NULL, WS_POPUP, ex_style); + MoveWindow(pos.x(), pos.y(), pos.width(), pos.height(), TRUE); + // To show tooltip on popup window.(e.g. title in <select>) + // Popups default to showing, which means |DidBecomeSelected()| isn't invoked. + // Ensure the tooltip is created otherwise tooltips are never shown. + EnsureTooltip(); + ShowWindow(IsActivatable() ? SW_SHOW : SW_SHOWNA); +} diff --git a/content/browser/renderer_host/render_widget_host_view_win.h b/content/browser/renderer_host/render_widget_host_view_win.h new file mode 100644 index 0000000..0f68b42 --- /dev/null +++ b/content/browser/renderer_host/render_widget_host_view_win.h @@ -0,0 +1,389 @@ +// Copyright (c) 2011 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. + +#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_WIN_H_ +#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_WIN_H_ +#pragma once + +#include <atlbase.h> +#include <atlapp.h> +#include <atlcrack.h> +#include <atlmisc.h> + +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/task.h" +#include "base/win/scoped_comptr.h" +#include "content/browser/accessibility/browser_accessibility_manager.h" +#include "content/browser/renderer_host/render_widget_host_view.h" +#include "content/common/notification_observer.h" +#include "content/common/notification_registrar.h" +#include "ui/base/win/ime_input.h" +#include "ui/gfx/native_widget_types.h" +#include "webkit/glue/webcursor.h" + +class BackingStore; +class RenderWidgetHost; + +namespace gfx { +class Size; +class Rect; +} + +namespace IPC { +class Message; +} + +namespace ui { +class ViewProp; +} + +typedef CWinTraits<WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0> + RenderWidgetHostHWNDTraits; + +extern const wchar_t kRenderWidgetHostHWNDClass[]; + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostViewWin +// +// An object representing the "View" of a rendered web page. This object is +// responsible for displaying the content of the web page, receiving windows +// messages, and containing plugins HWNDs. It is the implementation of the +// RenderWidgetHostView that the cross-platform RenderWidgetHost object uses +// to display the data. +// +// Comment excerpted from render_widget_host.h: +// +// "The lifetime of the RenderWidgetHostHWND is tied to the render process. +// If the render process dies, the RenderWidgetHostHWND goes away and all +// references to it must become NULL." +// +class RenderWidgetHostViewWin + : public CWindowImpl<RenderWidgetHostViewWin, + CWindow, + RenderWidgetHostHWNDTraits>, + public RenderWidgetHostView, + public NotificationObserver, + public BrowserAccessibilityDelegate { + public: + // The view will associate itself with the given widget. + explicit RenderWidgetHostViewWin(RenderWidgetHost* widget); + virtual ~RenderWidgetHostViewWin(); + + void CreateWnd(HWND parent); + + void ScheduleComposite(); + + IAccessible* GetIAccessible(); + + DECLARE_WND_CLASS_EX(kRenderWidgetHostHWNDClass, CS_DBLCLKS, 0); + + BEGIN_MSG_MAP(RenderWidgetHostHWND) + MSG_WM_CREATE(OnCreate) + MSG_WM_ACTIVATE(OnActivate) + MSG_WM_DESTROY(OnDestroy) + MSG_WM_PAINT(OnPaint) + MSG_WM_NCPAINT(OnNCPaint) + MSG_WM_ERASEBKGND(OnEraseBkgnd) + MSG_WM_SETCURSOR(OnSetCursor) + MSG_WM_SETFOCUS(OnSetFocus) + MSG_WM_KILLFOCUS(OnKillFocus) + MSG_WM_CAPTURECHANGED(OnCaptureChanged) + MSG_WM_CANCELMODE(OnCancelMode) + MSG_WM_INPUTLANGCHANGE(OnInputLangChange) + MSG_WM_THEMECHANGED(OnThemeChanged) + MSG_WM_NOTIFY(OnNotify) + MESSAGE_HANDLER(WM_IME_SETCONTEXT, OnImeSetContext) + MESSAGE_HANDLER(WM_IME_STARTCOMPOSITION, OnImeStartComposition) + MESSAGE_HANDLER(WM_IME_COMPOSITION, OnImeComposition) + MESSAGE_HANDLER(WM_IME_ENDCOMPOSITION, OnImeEndComposition) + MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseEvent) + MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseEvent) + MESSAGE_HANDLER(WM_LBUTTONDOWN, OnMouseEvent) + MESSAGE_HANDLER(WM_MBUTTONDOWN, OnMouseEvent) + MESSAGE_HANDLER(WM_RBUTTONDOWN, OnMouseEvent) + MESSAGE_HANDLER(WM_LBUTTONUP, OnMouseEvent) + MESSAGE_HANDLER(WM_MBUTTONUP, OnMouseEvent) + MESSAGE_HANDLER(WM_RBUTTONUP, OnMouseEvent) + MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnMouseEvent) + MESSAGE_HANDLER(WM_MBUTTONDBLCLK, OnMouseEvent) + MESSAGE_HANDLER(WM_RBUTTONDBLCLK, OnMouseEvent) + MESSAGE_HANDLER(WM_SYSKEYDOWN, OnKeyEvent) + MESSAGE_HANDLER(WM_SYSKEYUP, OnKeyEvent) + MESSAGE_HANDLER(WM_KEYDOWN, OnKeyEvent) + MESSAGE_HANDLER(WM_KEYUP, OnKeyEvent) + MESSAGE_HANDLER(WM_MOUSEWHEEL, OnWheelEvent) + MESSAGE_HANDLER(WM_MOUSEHWHEEL, OnWheelEvent) + MESSAGE_HANDLER(WM_HSCROLL, OnWheelEvent) + MESSAGE_HANDLER(WM_VSCROLL, OnWheelEvent) + MESSAGE_HANDLER(WM_CHAR, OnKeyEvent) + MESSAGE_HANDLER(WM_SYSCHAR, OnKeyEvent) + MESSAGE_HANDLER(WM_IME_CHAR, OnKeyEvent) + MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate) + MESSAGE_HANDLER(WM_GETOBJECT, OnGetObject) + MESSAGE_HANDLER(WM_PARENTNOTIFY, OnParentNotify) + END_MSG_MAP() + + // Implementation of RenderWidgetHostView: + virtual void InitAsPopup(RenderWidgetHostView* parent_host_view, + const gfx::Rect& pos) OVERRIDE; + virtual void InitAsFullscreen( + RenderWidgetHostView* reference_host_view) OVERRIDE; + virtual RenderWidgetHost* GetRenderWidgetHost() const OVERRIDE; + virtual void DidBecomeSelected() OVERRIDE; + virtual void WasHidden() OVERRIDE; + virtual void SetSize(const gfx::Size& size) OVERRIDE; + virtual void SetBounds(const gfx::Rect& rect) OVERRIDE; + virtual gfx::NativeView GetNativeView() const OVERRIDE; + virtual gfx::NativeViewId GetNativeViewId() const OVERRIDE; + virtual void MovePluginWindows( + const std::vector<webkit::npapi::WebPluginGeometry>& moves) OVERRIDE; + virtual void Focus() OVERRIDE; + virtual void Blur() OVERRIDE; + virtual bool HasFocus() OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual bool IsShowing() OVERRIDE; + virtual gfx::Rect GetViewBounds() const OVERRIDE; + virtual void UpdateCursor(const WebCursor& cursor) OVERRIDE; + virtual void SetIsLoading(bool is_loading) OVERRIDE; + virtual void ImeUpdateTextInputState(ui::TextInputType type, + bool can_compose_inline, + const gfx::Rect& caret_rect) OVERRIDE; + virtual void ImeCancelComposition() OVERRIDE; + virtual void DidUpdateBackingStore( + const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy, + const std::vector<gfx::Rect>& copy_rects) OVERRIDE; + virtual void RenderViewGone(base::TerminationStatus status, + int error_code) OVERRIDE; + // called by TabContents before DestroyWindow + virtual void WillWmDestroy() OVERRIDE; + virtual void Destroy() OVERRIDE; + virtual void SetTooltipText(const std::wstring& tooltip_text) OVERRIDE; + virtual BackingStore* AllocBackingStore(const gfx::Size& size) OVERRIDE; + virtual void SetBackground(const SkBitmap& background) OVERRIDE; + virtual void SetVisuallyDeemphasized(const SkColor* color, + bool animate) OVERRIDE; + virtual void UnhandledWheelEvent( + const WebKit::WebMouseWheelEvent& event) OVERRIDE; + virtual void SetHasHorizontalScrollbar( + bool has_horizontal_scrollbar) OVERRIDE; + virtual void SetScrollOffsetPinning( + bool is_pinned_to_left, bool is_pinned_to_right) OVERRIDE; + virtual gfx::PluginWindowHandle GetCompositingSurface() OVERRIDE; + virtual void ShowCompositorHostWindow(bool show) OVERRIDE; + virtual void OnAccessibilityNotifications( + const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params + ) OVERRIDE; + + // Implementation of NotificationObserver: + virtual void Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) OVERRIDE; + + // Implementation of BrowserAccessibilityDelegate: + virtual void SetAccessibilityFocus(int acc_obj_id) OVERRIDE; + virtual void AccessibilityDoDefaultAction(int acc_obj_id) OVERRIDE; + + protected: + // Windows Message Handlers + LRESULT OnCreate(CREATESTRUCT* create_struct); + void OnActivate(UINT, BOOL, HWND); + void OnDestroy(); + void OnPaint(HDC unused_dc); + void OnNCPaint(HRGN update_region); + LRESULT OnEraseBkgnd(HDC dc); + LRESULT OnSetCursor(HWND window, UINT hittest_code, UINT mouse_message_id); + void OnSetFocus(HWND window); + void OnKillFocus(HWND window); + void OnCaptureChanged(HWND window); + void OnCancelMode(); + void OnInputLangChange(DWORD character_set, HKL input_language_id); + void OnThemeChanged(); + LRESULT OnNotify(int w_param, NMHDR* header); + LRESULT OnImeSetContext( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnImeStartComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnImeComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnImeEndComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnMouseEvent( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnKeyEvent( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnWheelEvent( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled); + LRESULT OnMouseActivate(UINT message, + WPARAM wparam, + LPARAM lparam, + BOOL& handled); + // Handle MSAA requests for accessibility information. + LRESULT OnGetObject(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled); + // Handle vertical scrolling + LRESULT OnVScroll(int code, short position, HWND scrollbar_control); + // Handle horizontal scrolling + LRESULT OnHScroll(int code, short position, HWND scrollbar_control); + + LRESULT OnParentNotify(UINT message, WPARAM wparam, LPARAM lparam, + BOOL& handled); + + void OnFinalMessage(HWND window); + + private: + // Updates the display cursor to the current cursor if the cursor is over this + // render view. + void UpdateCursorIfOverSelf(); + + // Tells Windows that we want to hear about mouse exit messages. + void TrackMouseLeave(bool start_tracking); + + // Sends a message to the RenderView in the renderer process. + bool Send(IPC::Message* message); + + // Set the tooltip region to the size of the window, creating the tooltip + // hwnd if it has not been created yet. + void EnsureTooltip(); + + // Tooltips become invalid when the root ancestor changes. When the View + // becomes hidden, this method is called to reset the tooltip. + void ResetTooltip(); + + // Sends the specified mouse event to the renderer. + void ForwardMouseEventToRenderer(UINT message, WPARAM wparam, LPARAM lparam); + + // Synthesize mouse wheel event. + LRESULT SynthesizeMouseWheel(bool is_vertical, int scroll_code, + short scroll_position); + + // Shuts down the render_widget_host_. This is a separate function so we can + // invoke it from the message loop. + void ShutdownHost(); + + // Redraws the window synchronously, and any child windows (i.e. plugins) + // asynchronously. + void Redraw(); + + // Draw our background over the given HDC in the given |rect|. The background + // will be tiled such that it lines up with existing tiles starting from the + // origin of |dc|. + void DrawBackground(const RECT& rect, CPaintDC* dc); + + // Create an intermediate window between the given HWND and its parent. + HWND ReparentWindow(HWND window); + + // Clean up the compositor window, if needed. + void CleanupCompositorWindow(); + + // Whether the window should be activated. + bool IsActivatable() const; + + // Do initialization needed by both InitAsPopup() and InitAsFullscreen(). + void DoPopupOrFullscreenInit(HWND parent_hwnd, + const gfx::Rect& pos, + DWORD ex_style); + + // The associated Model. While |this| is being Destroyed, + // |render_widget_host_| is NULL and the Windows message loop is run one last + // time. Message handlers must check for a NULL |render_widget_host_|. + RenderWidgetHost* render_widget_host_; + + // When we are doing accelerated compositing + HWND compositor_host_window_; + + // true if the compositor host window must be hidden after the + // software renderered view is updated. + bool hide_compositor_window_at_next_paint_; + + // The cursor for the page. This is passed up from the renderer. + WebCursor current_cursor_; + + // Indicates if the page is loading. + bool is_loading_; + + // true if we are currently tracking WM_MOUSEEXIT messages. + bool track_mouse_leave_; + + // Wrapper class for IME input. + // (See "ui/base/win/ime_input.h" for its details.) + ui::ImeInput ime_input_; + + // Represents whether or not this browser process is receiving status + // messages about the focused edit control from a renderer process. + bool ime_notification_; + + // true if Enter was hit when render widget host was in focus. + bool capture_enter_key_; + + // true if the View is not visible. + bool is_hidden_; + + // True if we're in the midst of a paint operation and should respond to + // DidPaintRect() notifications by merely invalidating. See comments on + // render_widget_host_view.h:DidPaintRect(). + bool about_to_validate_and_paint_; + + // true if the View should be closed when its HWND is deactivated (used to + // support SELECT popups which are closed when they are deactivated). + bool close_on_deactivate_; + + // Whether Destroy() has been called. Used to detect a crasher + // (http://crbug.com/24248) where render_view_host_ has been deleted when + // OnFinalMessage is called. + bool being_destroyed_; + + // Tooltips + // The text to be shown in the tooltip, supplied by the renderer. + std::wstring tooltip_text_; + // The tooltip control hwnd + HWND tooltip_hwnd_; + // Whether or not a tooltip is currently visible. We use this to track + // whether or not we want to force-close the tooltip when we receive mouse + // move notifications from the renderer. See comment in OnMsgSetTooltipText. + bool tooltip_showing_; + + // Factory used to safely scope delayed calls to ShutdownHost(). + ScopedRunnableMethodFactory<RenderWidgetHostViewWin> shutdown_factory_; + + // Our parent HWND. We keep a reference to it as we SetParent(NULL) when + // hidden to prevent getting messages (Paint, Resize...), and we reattach + // when shown again. + HWND parent_hwnd_; + + // Instance of accessibility information for the root of the MSAA + // tree representation of the WebKit render tree. + scoped_ptr<BrowserAccessibilityManager> browser_accessibility_manager_; + + // The time at which this view started displaying white pixels as a result of + // not having anything to paint (empty backing store from renderer). This + // value returns true for is_null() if we are not recording whiteout times. + base::TimeTicks whiteout_start_time_; + + // The time it took after this view was selected for it to be fully painted. + base::TimeTicks tab_switch_paint_time_; + + // A color we use to shade the entire render view. If 100% transparent, we do + // not shade the render view. + SkColor overlay_color_; + + // Registrar so we can listen to RENDERER_PROCESS_TERMINATED events. + NotificationRegistrar registrar_; + + // Stores the current text input type received by ImeUpdateTextInputState() + // method. + ui::TextInputType text_input_type_; + + ScopedVector<ui::ViewProp> props_; + + // Is the widget fullscreen? + bool is_fullscreen_; + + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewWin); +}; + +#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_WIN_H_ diff --git a/content/content_browser.gypi b/content/content_browser.gypi index a78751a..b0f14bc 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -399,6 +399,8 @@ 'browser/renderer_host/render_widget_host_view.h', 'browser/renderer_host/render_widget_host_view_gtk.cc', 'browser/renderer_host/render_widget_host_view_gtk.h', + 'browser/renderer_host/render_widget_host_view_win.cc', + 'browser/renderer_host/render_widget_host_view_win.h', 'browser/renderer_host/resource_dispatcher_host.cc', 'browser/renderer_host/resource_dispatcher_host.h', 'browser/renderer_host/resource_dispatcher_host_delegate.h', |