diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/render_widget_host_hwnd.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/render_widget_host_hwnd.cc')
-rw-r--r-- | chrome/browser/render_widget_host_hwnd.cc | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/chrome/browser/render_widget_host_hwnd.cc b/chrome/browser/render_widget_host_hwnd.cc new file mode 100644 index 0000000..fb3b2b5 --- /dev/null +++ b/chrome/browser/render_widget_host_hwnd.cc @@ -0,0 +1,867 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/render_widget_host_hwnd.h" + +#include <windows.h> + +#include "base/command_line.h" +#include "base/gfx/bitmap_header.h" +#include "base/gfx/rect.h" +#include "base/histogram.h" +#include "base/win_util.h" +#include "chrome/browser/render_process_host.h" +// TODO(beng): (Cleanup) we should not need to include this file... see comment +// in |DidBecomeSelected|. +#include "chrome/browser/render_view_host.h" +#include "chrome/browser/render_widget_host.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/plugin_messages.h" +#include "chrome/common/win_util.h" +#include "chrome/views/hwnd_view_container.h" +#include "webkit/glue/webcursor.h" + +// Tooltips will wrap after this width. Yes, wrap. Imagine that! +static const int kTooltipMaxWidthPixels = 300; + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostHWND, public: + +RenderWidgetHostHWND::RenderWidgetHostHWND( + RenderWidgetHost* render_widget_host) + : RenderWidgetHostView(), + render_widget_host_(render_widget_host), + real_cursor_(LoadCursor(NULL, IDC_ARROW)), + real_cursor_type_(WebCursor::ARROW), + track_mouse_leave_(false), + ime_notification_(false), + is_hidden_(false), + close_on_deactivate_(false), + tooltip_hwnd_(NULL), + tooltip_showing_(false), + shutdown_factory_(this), + parent_hwnd_(NULL), + is_loading_(false) { +} + +RenderWidgetHostHWND::~RenderWidgetHostHWND() { + if (real_cursor_type_ == WebCursor::CUSTOM) + DestroyIcon(real_cursor_); + ResetTooltip(); +} + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostHWND, RenderWidgetHostView implementation: + +void RenderWidgetHostHWND::DidBecomeSelected() { + if (!is_hidden_) + return; + + is_hidden_ = false; + EnsureTooltip(); + render_widget_host_->WasRestored(); +} + +void RenderWidgetHostHWND::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 RenderWidgetHostHWND::SetSize(const gfx::Size& size) { + if (is_hidden_) + return; + + UINT swp_flags = SWP_NOSENDCHANGING | SWP_NOOWNERZORDER | SWP_NOCOPYBITS | + SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | + SWP_DEFERERASE; + SetWindowPos(NULL, 0, 0, size.width(), size.height(), swp_flags); + render_widget_host_->WasResized(); + EnsureTooltip(); +} + +HWND RenderWidgetHostHWND::GetPluginHWND() { + return m_hWnd; +} + +void RenderWidgetHostHWND::ForwardMouseEventToRenderer(UINT message, + WPARAM wparam, + LPARAM lparam) { + WebMouseEvent event(m_hWnd, message, wparam, lparam); + switch (event.type) { + case WebInputEvent::MOUSE_MOVE: + TrackMouseLeave(true); + break; + case WebInputEvent::MOUSE_LEAVE: + TrackMouseLeave(false); + break; + case WebInputEvent::MOUSE_DOWN: + SetCapture(); + break; + case WebInputEvent::MOUSE_UP: + if (GetCapture() == m_hWnd) + ReleaseCapture(); + break; + } + + render_widget_host_->ForwardMouseEvent(event); + + if (event.type == WebInputEvent::MOUSE_DOWN) { + // 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 RenderWidgetHostHWND::Focus() { + if (IsWindow()) + SetFocus(); +} + +void RenderWidgetHostHWND::Blur() { + ChromeViews::FocusManager* focus_manager = + ChromeViews::FocusManager::GetFocusManager(GetParent()); + // We don't have a FocusManager if we are hidden. + if (focus_manager && render_widget_host_->CanBlur()) + focus_manager->ClearFocus(); +} + +bool RenderWidgetHostHWND::HasFocus() { + return ::GetFocus() == m_hWnd; +} + +void RenderWidgetHostHWND::Show() { + DCHECK(parent_hwnd_); + SetParent(parent_hwnd_); + ShowWindow(SW_SHOW); + + DidBecomeSelected(); +} + +void RenderWidgetHostHWND::Hide() { + if (::GetFocus() == m_hWnd) + ::SetFocus(NULL); + ShowWindow(SW_HIDE); + parent_hwnd_ = GetParent(); + // Orphan the window so we stop receiving messages. + SetParent(NULL); + + WasHidden(); +} + +gfx::Rect RenderWidgetHostHWND::GetViewBounds() const { + CRect window_rect; + GetWindowRect(&window_rect); + return gfx::Rect(window_rect); +} + +void RenderWidgetHostHWND::UpdateCursor(const WebCursor& cursor) { + static HINSTANCE module_handle = + GetModuleHandle(chrome::kBrowserResourcesDll); + + // If the last active cursor was a custom cursor, we need to destroy + // it before setting the new one. + if (real_cursor_type_ == WebCursor::CUSTOM) + DestroyIcon(real_cursor_); + + real_cursor_type_ = cursor.type(); + if (real_cursor_type_ == cursor.WebCursor::CUSTOM) { + real_cursor_ = cursor.GetCustomCursor(); + } else { + // 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. + real_cursor_ = cursor.GetCursor(module_handle); + } + + UpdateCursorIfOverSelf(); +} + +void RenderWidgetHostHWND::UpdateCursorIfOverSelf() { + static HINSTANCE module_handle = + GetModuleHandle(chrome::kBrowserResourcesDll); + + HCURSOR display_cursor = real_cursor_; + // 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_ && (real_cursor_type_ == WebCursor::ARROW)) { + WebCursor page_loading_cursor(WebCursor::APPSTARTING); + display_cursor = page_loading_cursor.GetCursor(module_handle); + } + + // If the mouse is over our HWND, then update the cursor state immediately. + CPoint pt; + GetCursorPos(&pt); + if (WindowFromPoint(pt) == m_hWnd) + SetCursor(display_cursor); +} + +void RenderWidgetHostHWND::SetIsLoading(bool is_loading) { + is_loading_ = is_loading; + UpdateCursorIfOverSelf(); +} + +void RenderWidgetHostHWND::IMEUpdateStatus(ViewHostMsg_ImeControl control, + int x, int y) { + if (control == IME_DISABLE) { + ime_input_.DisableIME(m_hWnd); + } else { + ime_input_.EnableIME(m_hWnd, x, y, control == IME_COMPLETE_COMPOSITION); + } +} + +void RenderWidgetHostHWND::DidPaintRect(const gfx::Rect& rect) { + if (is_hidden_) + return; + + RECT invalid_rect = rect.ToRECT(); + + // 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). + // + // Please refer to the RenderWidgetHostHWND::DidScrollRect function for the + // reasoning behind the combination of flags passed to RedrawWindow. + // + RedrawWindow(&invalid_rect, NULL, + RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_FRAME); +} + +void RenderWidgetHostHWND::DidScrollRect( + const gfx::Rect& rect, int dx, int dy) { + if (is_hidden_) + return; + + // We need to pass in SW_INVALIDATE to ScrollWindowEx. The MSDN + // documentation states that it only applies to the HRGN argument, which is + // wrong. Not passing in this flag does not invalidate the region which was + // scrolled from, thus causing painting issues. + RECT clip_rect = rect.ToRECT(); + ScrollWindowEx(dx, dy, NULL, &clip_rect, NULL, NULL, SW_INVALIDATE); + + RECT invalid_rect = {0}; + GetUpdateRect(&invalid_rect); + + // 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). + // + // Our RenderWidgetHostHWND does not have a non-client area, whereas the + // children (plugin windows) may. If we don't pass in RDW_FRAME then the + // children don't receive WM_NCPAINT messages while scrolling, which causes + // painting problems (http://b/issue?id=923945). We need to pass + // RDW_INVALIDATE as it is required for RDW_FRAME to work. + // + RedrawWindow(&invalid_rect, NULL, + RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_FRAME); +} + +void RenderWidgetHostHWND::RendererGone() { + // TODO(darin): keep this around, and draw sad-tab into it. + UpdateCursorIfOverSelf(); + DestroyWindow(); +} + +void RenderWidgetHostHWND::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; + DestroyWindow(); +} + +void RenderWidgetHostHWND::SetTooltipText(const std::wstring& tooltip_text) { + if (tooltip_text != tooltip_text_) { + tooltip_text_ = 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); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// RenderWidgetHostHWND, private: + +LRESULT RenderWidgetHostHWND::OnCreate(CREATESTRUCT* create_struct) { + // Call the WM_INPUTLANGCHANGE message handler to initialize the input locale + // of a browser process. + OnInputLangChange(0, 0); + return 0; +} + +void RenderWidgetHostHWND::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 RenderWidgetHostHWND::OnDestroy() { + ResetTooltip(); + TrackMouseLeave(false); +} + +void RenderWidgetHostHWND::OnPaint(HDC dc) { + DCHECK(render_widget_host_->process()->channel()); + + CPaintDC paint_dc(m_hWnd); + HBRUSH white_brush = reinterpret_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)); + + RenderWidgetHost::BackingStore* backing_store = + render_widget_host_->GetBackingStore(); + + if (backing_store) { + gfx::Rect damaged_rect(paint_dc.m_ps.rcPaint); + + gfx::Rect bitmap_rect( + 0, 0, backing_store->size().width(), backing_store->size().height()); + + gfx::Rect paint_rect = bitmap_rect.Intersect(damaged_rect); + if (!paint_rect.IsEmpty()) { + BitBlt(paint_dc.m_hDC, + paint_rect.x(), + paint_rect.y(), + paint_rect.width(), + paint_rect.height(), + backing_store->dc(), + paint_rect.x(), + paint_rect.y(), + SRCCOPY); + } + + // Fill the remaining portion of the damaged_rect with white + 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()); + paint_dc.FillRect(&r, white_brush); + } + 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(); + paint_dc.FillRect(&r, white_brush); + } + if (!whiteout_start_time_.is_null()) { + TimeDelta whiteout_duration = TimeTicks::Now() - whiteout_start_time_; + UMA_HISTOGRAM_TIMES(L"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(); + } + } else { + paint_dc.FillRect(&paint_dc.m_ps.rcPaint, white_brush); + if (whiteout_start_time_.is_null()) + whiteout_start_time_ = TimeTicks::Now(); + } +} + +void RenderWidgetHostHWND::OnNCPaint(HRGN update_region) { + // Do nothing. This suppresses the resize corner that Windows would + // otherwise draw for us. +} + +LRESULT RenderWidgetHostHWND::OnEraseBkgnd(HDC dc) { + return 1; +} + +LRESULT RenderWidgetHostHWND::OnSetCursor(HWND window, UINT hittest_code, + UINT mouse_message_id) { + UpdateCursorIfOverSelf(); + return 0; +} + +void RenderWidgetHostHWND::OnSetFocus(HWND window) { + render_widget_host_->Focus(); +} + +void RenderWidgetHostHWND::OnKillFocus(HWND window) { + render_widget_host_->Blur(); +} + +void RenderWidgetHostHWND::OnCaptureChanged(HWND window) { + render_widget_host_->LostCapture(); +} + +void RenderWidgetHostHWND::OnCancelMode() { + render_widget_host_->LostCapture(); + + if (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( + &RenderWidgetHostHWND::ShutdownHost)); + } +} + +void RenderWidgetHostHWND::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 (Send(new ViewMsg_ImeSetInputMode(render_widget_host_->routing_id(), + ime_status))) { + ime_notification_ = ime_status; + } + } +} + +LRESULT RenderWidgetHostHWND::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 RenderWidgetHostHWND::OnImeSetContext( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + // 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 (Send(new ViewMsg_ImeSetInputMode( + render_widget_host_->routing_id(), activated))) { + ime_notification_ = activated; + } + + if (ime_notification_) + ime_input_.CreateImeWindow(m_hWnd); + + ime_input_.CleanupComposition(m_hWnd); + ime_input_.SetImeWindowStyle(m_hWnd, message, wparam, lparam, &handled); + return 0; +} + +LRESULT RenderWidgetHostHWND::OnImeStartComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + // 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 RenderWidgetHostHWND::OnImeComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + // At first, update the position of the IME window. + ime_input_.UpdateImeWindow(m_hWnd); + + // Retrieve the result string and its attributes of the ongoing composition + // and send it to a renderer process. + ImeComposition composition; + if (ime_input_.GetResult(m_hWnd, lparam, &composition)) { + Send(new ViewMsg_ImeSetComposition(render_widget_host_->routing_id(), + composition.string_type, + composition.cursor_position, + composition.target_start, + composition.target_end, + composition.ime_string)); + 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)) { + Send(new ViewMsg_ImeSetComposition(render_widget_host_->routing_id(), + composition.string_type, + composition.cursor_position, + composition.target_start, + composition.target_end, + composition.ime_string)); + } + // 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 RenderWidgetHostHWND::OnImeEndComposition( + UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { + 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. + std::wstring empty_string; + Send(new ViewMsg_ImeSetComposition(render_widget_host_->routing_id(), + 0, -1, -1, -1, empty_string)); + ime_input_.ResetComposition(m_hWnd); + } + ime_input_.DestroyImeWindow(m_hWnd); + // Let WTL call ::DefWindowProc() and release its resources. + handled = FALSE; + return 0; +} + +LRESULT RenderWidgetHostHWND::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 + // WebContents first in the case of popups. If we do, we would need to + // convert the click from the popup window coordinates to the WebContents' + // 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 WebContents window would have to be specified to the + // RenderViewHostHWND as there is no way to retrieve it from the HWND. + if (!close_on_deactivate_) { // Don't forward if the container is a popup. + switch (message) { + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_MOUSEMOVE: + case WM_MOUSELEAVE: + case WM_RBUTTONDOWN: { + // Give the WebContents first crack at the message. It may want to + // prevent forwarding to the renderer if some higher level browser + // functionality is invoked. + if (SendMessage(GetParent(), message, wparam, lparam) != 0) + return 1; + } + } + } + + ForwardMouseEventToRenderer(message, wparam, lparam); + return 0; +} + +LRESULT RenderWidgetHostHWND::OnKeyEvent(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + handled = TRUE; + + // 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); + } + + render_widget_host_->ForwardKeyboardEvent( + WebKeyboardEvent(m_hWnd, message, wparam, lparam)); + return 0; +} + +LRESULT RenderWidgetHostHWND::OnWheelEvent(UINT message, WPARAM wparam, + LPARAM lparam, BOOL& handled) { + // 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 WebContents-specific functionality... + bool handled_by_webcontents = false; + if (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_webcontents = + !!::SendMessage(GetParent(), ChromeViews::kReflectedMessage, 0, + reinterpret_cast<LPARAM>(&new_message)); + } + + if (!handled_by_webcontents) { + render_widget_host_->ForwardWheelEvent( + WebMouseWheelEvent(m_hWnd, message, wparam, lparam)); + } + handled = TRUE; + return 0; +} + +LRESULT RenderWidgetHostHWND::OnNcCalcSize(UINT message, WPARAM w_param, + LPARAM l_param, BOOL& handled) { + // Handle WM_NCCALCSIZE and make scrollbar size to 0. + // Here we indicate that the entire window area is our + // client area. The assumption is that we won't have a border + // or any other non-client widget. + return 0; +} + +LRESULT RenderWidgetHostHWND::OnSize(UINT message, WPARAM w_param, + LPARAM l_param, BOOL& handled) { + // Set arbitrary but valid scroll information so that + // our window will get WS_VSCROLL and WS_HSCROLL style. + + // TODO(joshia): The correct thing to do here is to get + // the correct scroll information from the renderer and + // set it here. + SCROLLINFO si = {0}; + si.cbSize = sizeof(si); + si.fMask = SIF_ALL; + + si.nMin = 1; + si.nMax = 100; + si.nPage = 10; + si.nTrackPos = 50; + + SetScrollInfo(SB_HORZ, &si, FALSE); + SetScrollInfo(SB_VERT, &si, FALSE); + + handled = FALSE; + return 0; +} + +LRESULT RenderWidgetHostHWND::OnMouseActivate(UINT, WPARAM, LPARAM, + BOOL& handled) { + // 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); + MapWindowPoints(m_hWnd, &cursor_pos, 1); + HWND child_window = ::RealChildWindowFromPoint(m_hWnd, cursor_pos); + if (::IsWindow(child_window)) { + ::SetFocus(child_window); + return MA_NOACTIVATE; + } else { + handled = FALSE; + return MA_ACTIVATE; + } +} + +void RenderWidgetHostHWND::OnFinalMessage(HWND window) { + render_widget_host_->ViewDestroyed(); + delete this; +} + +void RenderWidgetHostHWND::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 RenderWidgetHostHWND::Send(IPC::Message* message) { + return render_widget_host_->Send(message); +} + +void RenderWidgetHostHWND::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); + ti.uFlags = TTF_TRANSPARENT; + ti.lpszText = LPSTR_TEXTCALLBACK; + } + + CRect cr; + GetClientRect(&ti.rect); + SendMessage(tooltip_hwnd_, message, NULL, reinterpret_cast<LPARAM>(&ti)); +} + +void RenderWidgetHostHWND::ResetTooltip() { + if (::IsWindow(tooltip_hwnd_)) + ::DestroyWindow(tooltip_hwnd_); + tooltip_hwnd_ = NULL; +} + +BOOL CALLBACK RenderWidgetHostHWND::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; +} + +void RenderWidgetHostHWND::ShutdownHost() { + shutdown_factory_.RevokeAll(); + render_widget_host_->Shutdown(); + // Do not touch any members at this point, |this| has been deleted. +} |