<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/render_widget_host_view_win.h"

#include <algorithm>
#include <peninputpanel_i.c>

#include "base/bind.h"
#include "base/bind_helpers.h"
#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/win_util.h"
#include "base/win/windows_version.h"
#include "base/win/wrapped_window_proc.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "content/browser/accessibility/browser_accessibility_win.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/gpu/gpu_process_host_ui_shim.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_impl.h"
#include "content/browser/renderer_host/render_widget_host.h"
#include "content/common/accessibility_messages.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/common/plugin_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/page_zoom.h"
#include "content/public/common/process_type.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/base/win/mouse_wheel_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/scoped_sk_region.h"
#include "ui/gfx/screen.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 content::BrowserThread;
using content::RenderWidgetHostView;
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;

// In mouse lock mode, we need to prevent the (invisible) cursor from hitting
// the border of the view, in order to get valid movement information. However,
// forcing the cursor back to the center of the view after each mouse move
// doesn't work well. It reduces the frequency of useful WM_MOUSEMOVE messages
// significantly. Therefore, we move the cursor to the center of the view only
// if it approaches the border. |kMouseLockBorderPercentage| specifies the width
// of the border area, in percentage of the corresponding dimension.
const int kMouseLockBorderPercentage = 15;

// We display the onscreen keyboard in the context of a WM_POINTERDOWN message.
// This context is reset in a delayed task. Theory being that the page should
// have enough time to change focus before this context is reset.
// TODO(ananta)
// Refine this.
const int kPointerDownContextResetDelay = 200;

// A callback function for EnumThreadWindows to enumerate and dismiss
// any owned popup 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;
}

// |window| is the plugin HWND, created and destroyed in the plugin process.
// |parent| is the parent HWND, created and destroyed on the browser UI thread.
void NotifyPluginProcessHostHelper(HWND window, HWND parent, int tries) {
  // How long to wait between each try.
  static const int kTryDelayMs = 200;

  DWORD plugin_process_id;
  bool found_starting_plugin_process = false;
  GetWindowThreadProcessId(window, &plugin_process_id);
  for (PluginProcessHostIterator iter; !iter.Done(); ++iter) {
    if (!iter.GetData().handle) {
      found_starting_plugin_process = true;
      continue;
    }
    if (base::GetProcId(iter.GetData().handle) == plugin_process_id) {
      iter->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 > 0) {
      MessageLoop::current()->PostDelayedTask(
          FROM_HERE,
          base::Bind(&NotifyPluginProcessHostHelper, window, parent, tries - 1),
          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);
}

// 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;
}

// 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);
}

void SendToGpuProcessHost(int gpu_host_id, scoped_ptr<IPC::Message> message) {
  GpuProcessHost* gpu_process_host = GpuProcessHost::FromID(gpu_host_id);
  if (!gpu_process_host)
    return;

  gpu_process_host->Send(message.release());
}

void PostTaskOnIOThread(const tracked_objects::Location& from_here,
                        base::Closure task) {
  BrowserThread::PostTask(BrowserThread::IO, from_here, task);
}

bool DecodeZoomGesture(HWND hwnd, const GESTUREINFO& gi,
                       content::PageZoom* zoom,
                       POINT* zoom_center) {
  static long start = 0;
  static POINT zoom_first;

  if (gi.dwFlags == GF_BEGIN) {
    start = gi.ullArguments;
    zoom_first.x = gi.ptsLocation.x;
    zoom_first.y = gi.ptsLocation.y;
    ScreenToClient(hwnd, &zoom_first);
    return false;
  }

  if (gi.dwFlags == GF_END)
    return false;

  POINT zoom_second = {0};
  zoom_second.x = gi.ptsLocation.x;
  zoom_second.y = gi.ptsLocation.y;
  ScreenToClient(hwnd, &zoom_second);

  if (zoom_first.x == zoom_second.x && zoom_first.y == zoom_second.y)
    return false;

  zoom_center->x = (zoom_first.x + zoom_second.x) / 2;
  zoom_center->y = (zoom_first.y + zoom_second.y) / 2;

  double zoom_factor =
      static_cast<double>(gi.ullArguments)/static_cast<double>(start);

  *zoom = zoom_factor >= 1 ? content::PAGE_ZOOM_IN :
              content::PAGE_ZOOM_OUT;

  start = gi.ullArguments;
  zoom_first = zoom_second;
  return true;
}

bool DecodeScrollGesture(const GESTUREINFO& gi,
                         POINT* start,
                         POINT* delta){
  // Windows gestures are streams of messages with begin/end messages that
  // separate each new gesture. We key off the begin message to reset
  // the static variables.
  static POINT last_pt;
  static POINT start_pt;

  if (gi.dwFlags == GF_BEGIN) {
    delta->x = 0;
    delta->y = 0;
    start_pt.x = gi.ptsLocation.x;
    start_pt.y = gi.ptsLocation.y;
  } else {
    delta->x = gi.ptsLocation.x - last_pt.x;
    delta->y = gi.ptsLocation.y - last_pt.y;
  }
  last_pt.x = gi.ptsLocation.x;
  last_pt.y = gi.ptsLocation.y;
  *start = start_pt;
  return true;
}

WebKit::WebMouseWheelEvent MakeFakeScrollWheelEvent(HWND hwnd,
                                                    POINT start,
                                                    POINT delta) {
    WebKit::WebMouseWheelEvent result;
    result.type = WebInputEvent::MouseWheel;
    result.timeStampSeconds = ::GetMessageTime() / 1000.0;
    result.button = WebMouseEvent::ButtonNone;
    result.globalX = start.x;
    result.globalY = start.y;
    // Map to window coordinates.
    POINT client_point = { result.globalX, result.globalY };
    MapWindowPoints(0, hwnd, &client_point, 1);
    result.x = client_point.x;
    result.y = client_point.y;
    result.windowX = result.x;
    result.windowY = result.y;
    // Note that we support diagonal scrolling.
    result.deltaX = static_cast<float>(delta.x);
    result.wheelTicksX = WHEEL_DELTA;
    result.deltaY = static_cast<float>(delta.y);
    result.wheelTicksY = WHEEL_DELTA;
    return result;
}

static const int kTouchMask = 0x7;

inline int GetTouchType(const TOUCHINPUT& point) {
  return point.dwFlags & kTouchMask;
}

inline void SetTouchType(TOUCHINPUT* point, int type) {
  point->dwFlags = (point->dwFlags & kTouchMask) | type;
}

}  // namespace

///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewWin, public:

RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* 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),
      weak_factory_(this),
      parent_hwnd_(NULL),
      is_loading_(false),
      text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
      is_fullscreen_(false),
      ignore_mouse_movement_(true),
      composition_range_(ui::Range::InvalidRange()),
      touch_state_(this),
      pointer_down_context_(false),
      focus_on_editable_field_(false),
      received_focus_change_after_pointer_down_(false),
      transparent_region_(0) {
  render_widget_host_ = static_cast<RenderWidgetHostImpl*>(widget);
  render_widget_host_->SetView(this);
  registrar_.Add(this,
                 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
                 content::NotificationService::AllBrowserContextsAndSources());
  registrar_.Add(this,
                 content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
                 content::NotificationService::AllBrowserContextsAndSources());
}

RenderWidgetHostViewWin::~RenderWidgetHostViewWin() {
  UnlockMouse();
  ResetTooltip();
}

void RenderWidgetHostViewWin::CreateWnd(HWND parent) {
  // ATL function to create the window.
  Create(parent);
  if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
    // MSDN recommends using the ITextInputPanel interface to display the on
    // screen keyboard. This interface does not work predictably due to focus
    // and activation bugs. We use the deprecated IPenInputPanel interface for
    // now which works reliably with the caveat being that the keyboard cannot
    // be closed via the system close menu. Esc or clicking elsewhere works.
    // TODO(ananta): Revisit this.
    virtual_keyboard_.CreateInstance(CLSID_PenInputPanel, NULL, CLSCTX_INPROC);
    if (virtual_keyboard_) {
      virtual_keyboard_->put_AttachedEditWindow(
          reinterpret_cast<int>(m_hWnd));
      virtual_keyboard_->put_DefaultPanel(PT_Keyboard);
      virtual_keyboard_->put_Visible(VARIANT_FALSE);
      virtual_keyboard_->put_AutoShow(VARIANT_FALSE);
    } else {
      NOTREACHED() << "Failed to create instance of pen input panel";
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewWin, RenderWidgetHostView implementation:

void RenderWidgetHostViewWin::InitAsChild(
    gfx::NativeView parent_view) {
  parent_hwnd_ = parent_view;
  CreateWnd(parent_view);
}

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_| may be NULL if the TabContents is in the process of
  // closing.
  if (render_widget_host_)
    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.
  if (render_widget_host_)
    render_widget_host_->WasHidden();
}

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);
}

gfx::NativeViewAccessible
RenderWidgetHostViewWin::GetNativeViewAccessible() {
  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 (!GetBrowserAccessibilityManager()) {
    // Return busy document tree while renderer accessibility tree loads.
    WebAccessibility::State busy_state =
        static_cast<WebAccessibility::State>(1 << WebAccessibility::STATE_BUSY);
    SetBrowserAccessibilityManager(
        BrowserAccessibilityManager::CreateEmptyDocument(
            m_hWnd, busy_state, this));
  }

  return GetBrowserAccessibilityManager()->GetRoot()->
      toBrowserAccessibilityWin();
}

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
    ChangeWindowMessageFilterEx(parent, WM_MOUSEWHEEL, MSGFLT_ALLOW, NULL);
    ChangeWindowMessageFilterEx(parent, WM_GESTURE, MSGFLT_ALLOW, NULL);
    ChangeWindowMessageFilterEx(parent, WM_APPCOMMAND, MSGFLT_ALLOW, NULL);
    ::RemovePropW(orig_parent, webkit::npapi::kNativeWindowClassFilterProp);
  }
  ::SetParent(window, parent);
  // How many times we try to find a PluginProcessHost whose process matches
  // the HWND.
  static const int kMaxTries = 5;
  BrowserThread::PostTask(
      BrowserThread::IO,
      FROM_HERE,
      base::Bind(&NotifyPluginProcessHostHelper, window, parent, kMaxTries));
  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,
      base::Bind(base::IgnoreResult(&::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() {
  NOTREACHED();
}

bool RenderWidgetHostViewWin::HasFocus() const {
  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) {
    // 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::TextInputStateChanged(
    ui::TextInputType type,
    bool can_compose_inline) {
  // 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);
  }
}

void RenderWidgetHostViewWin::SelectionBoundsChanged(
    const gfx::Rect& start_rect,
    const gfx::Rect& end_rect) {
  bool is_enabled = (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
      text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD);
  // Only update caret position if the input method is enabled.
  if (is_enabled)
    ime_input_.UpdateCaretRect(m_hWnd, start_rect.Union(end_rect));
}

void RenderWidgetHostViewWin::ImeCancelComposition() {
  ime_input_.CancelIME(m_hWnd);
}

void RenderWidgetHostViewWin::ImeCompositionRangeChanged(
    const ui::Range& range) {
  composition_range_ = range;
}

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(&copy_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) {
  UpdateCursorIfOverSelf();
  Destroy();
}

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 string16& 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 string16 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) {
  content::RenderWidgetHostViewBase::SetBackground(background);
  render_widget_host_->SetBackground(background);
}

void RenderWidgetHostViewWin::UnhandledWheelEvent(
    const WebKit::WebMouseWheelEvent& event) {
}

void RenderWidgetHostViewWin::ProcessTouchAck(bool processed) {
}

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(ui::SetWindowSupportsRerouteMouseWheel(m_hWnd));

  if (base::win::GetVersion() >= base::win::VERSION_WIN7) {
    // Use gestures if touch event switch isn't present or registration fails.
    if (!CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kEnableTouchEvents) ||
        !RegisterTouchWindow(m_hWnd, TWF_WANTPALM)) {
      // Single finger panning is consistent with other windows applications.
      const DWORD gesture_allow = GC_PAN_WITH_SINGLE_FINGER_VERTICALLY |
                                  GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
      const DWORD gesture_block = GC_PAN_WITH_GUTTER;
      GESTURECONFIG gc[] = {
          { GID_ZOOM, GC_ZOOM, 0 },
          { GID_PAN, gesture_allow , gesture_block},
          { GID_TWOFINGERTAP, GC_TWOFINGERTAP , 0},
          { GID_PRESSANDTAP, GC_PRESSANDTAP , 0}
      };
      if (!SetGestureConfig(m_hWnd, 0, arraysize(gc), gc,
          sizeof(GESTURECONFIG))) {
        NOTREACHED();
      }
    }
  }
  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();

  if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
      IsTouchWindow(m_hWnd, NULL)) {
    UnregisterTouchWindow(m_hWnd);
  }

  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;
    RGNDATA* region_data = NULL;
    RECT* region_rects = NULL;

    if (data_size) {
      region_data_buf.reset(new char[data_size]);
      region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get());
      region_rects = reinterpret_cast<RECT*>(region_data->Buffer);
      data_size = GetRegionData(damage_region, data_size, region_data);
    }

    if (!data_size) {
      // Grabbing the damaged regions failed, fake with the whole rect.
      data_size = sizeof(RGNDATAHEADER) + sizeof(RECT);
      region_data_buf.reset(new char[data_size]);
      region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get());
      region_rects = reinterpret_cast<RECT*>(region_data->Buffer);
      region_data->rdh.nCount = 1;
      region_rects[0] = damaged_rect.ToRECT();
    }

    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()) {
        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::Rect dirty_area(dirty_rect);
    gfx::CanvasSkia canvas(dirty_area.size(), true);
    canvas.Translate(gfx::Point().Subtract(dirty_area.origin()));

    gfx::Rect dc_rect(dc->m_ps.rcPaint);
    canvas.TileImageInt(background_, 0, 0, dc_rect.width(), dc_rect.height());

    skia::DrawToNativeContext(canvas.sk_canvas(), *dc, dirty_area.x(),
                              dirty_area.y(), 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.
}

void RenderWidgetHostViewWin::SetClickthroughRegion(SkRegion* region) {
  if (transparent_region_.Get())
    transparent_region_.release();
  transparent_region_.Set(region);<commit_msg>Fix memory leak in RenderWidgetHostViewWin::SetTransparentRegion.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/render_widget_host_view_win.h"

#include <algorithm>
#include <peninputpanel_i.c>

#include "base/bind.h"
#include "base/bind_helpers.h"
#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/win_util.h"
#include "base/win/windows_version.h"
#include "base/win/wrapped_window_proc.h"
#include "content/browser/accessibility/browser_accessibility_state_impl.h"
#include "content/browser/accessibility/browser_accessibility_win.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/gpu/gpu_process_host_ui_shim.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_impl.h"
#include "content/browser/renderer_host/render_widget_host.h"
#include "content/common/accessibility_messages.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/common/plugin_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_types.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/page_zoom.h"
#include "content/public/common/process_type.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/base/win/mouse_wheel_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/scoped_sk_region.h"
#include "ui/gfx/screen.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 content::BrowserThread;
using content::RenderWidgetHostView;
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;

// In mouse lock mode, we need to prevent the (invisible) cursor from hitting
// the border of the view, in order to get valid movement information. However,
// forcing the cursor back to the center of the view after each mouse move
// doesn't work well. It reduces the frequency of useful WM_MOUSEMOVE messages
// significantly. Therefore, we move the cursor to the center of the view only
// if it approaches the border. |kMouseLockBorderPercentage| specifies the width
// of the border area, in percentage of the corresponding dimension.
const int kMouseLockBorderPercentage = 15;

// We display the onscreen keyboard in the context of a WM_POINTERDOWN message.
// This context is reset in a delayed task. Theory being that the page should
// have enough time to change focus before this context is reset.
// TODO(ananta)
// Refine this.
const int kPointerDownContextResetDelay = 200;

// A callback function for EnumThreadWindows to enumerate and dismiss
// any owned popup 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;
}

// |window| is the plugin HWND, created and destroyed in the plugin process.
// |parent| is the parent HWND, created and destroyed on the browser UI thread.
void NotifyPluginProcessHostHelper(HWND window, HWND parent, int tries) {
  // How long to wait between each try.
  static const int kTryDelayMs = 200;

  DWORD plugin_process_id;
  bool found_starting_plugin_process = false;
  GetWindowThreadProcessId(window, &plugin_process_id);
  for (PluginProcessHostIterator iter; !iter.Done(); ++iter) {
    if (!iter.GetData().handle) {
      found_starting_plugin_process = true;
      continue;
    }
    if (base::GetProcId(iter.GetData().handle) == plugin_process_id) {
      iter->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 > 0) {
      MessageLoop::current()->PostDelayedTask(
          FROM_HERE,
          base::Bind(&NotifyPluginProcessHostHelper, window, parent, tries - 1),
          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);
}

// 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;
}

// 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);
}

void SendToGpuProcessHost(int gpu_host_id, scoped_ptr<IPC::Message> message) {
  GpuProcessHost* gpu_process_host = GpuProcessHost::FromID(gpu_host_id);
  if (!gpu_process_host)
    return;

  gpu_process_host->Send(message.release());
}

void PostTaskOnIOThread(const tracked_objects::Location& from_here,
                        base::Closure task) {
  BrowserThread::PostTask(BrowserThread::IO, from_here, task);
}

bool DecodeZoomGesture(HWND hwnd, const GESTUREINFO& gi,
                       content::PageZoom* zoom,
                       POINT* zoom_center) {
  static long start = 0;
  static POINT zoom_first;

  if (gi.dwFlags == GF_BEGIN) {
    start = gi.ullArguments;
    zoom_first.x = gi.ptsLocation.x;
    zoom_first.y = gi.ptsLocation.y;
    ScreenToClient(hwnd, &zoom_first);
    return false;
  }

  if (gi.dwFlags == GF_END)
    return false;

  POINT zoom_second = {0};
  zoom_second.x = gi.ptsLocation.x;
  zoom_second.y = gi.ptsLocation.y;
  ScreenToClient(hwnd, &zoom_second);

  if (zoom_first.x == zoom_second.x && zoom_first.y == zoom_second.y)
    return false;

  zoom_center->x = (zoom_first.x + zoom_second.x) / 2;
  zoom_center->y = (zoom_first.y + zoom_second.y) / 2;

  double zoom_factor =
      static_cast<double>(gi.ullArguments)/static_cast<double>(start);

  *zoom = zoom_factor >= 1 ? content::PAGE_ZOOM_IN :
              content::PAGE_ZOOM_OUT;

  start = gi.ullArguments;
  zoom_first = zoom_second;
  return true;
}

bool DecodeScrollGesture(const GESTUREINFO& gi,
                         POINT* start,
                         POINT* delta){
  // Windows gestures are streams of messages with begin/end messages that
  // separate each new gesture. We key off the begin message to reset
  // the static variables.
  static POINT last_pt;
  static POINT start_pt;

  if (gi.dwFlags == GF_BEGIN) {
    delta->x = 0;
    delta->y = 0;
    start_pt.x = gi.ptsLocation.x;
    start_pt.y = gi.ptsLocation.y;
  } else {
    delta->x = gi.ptsLocation.x - last_pt.x;
    delta->y = gi.ptsLocation.y - last_pt.y;
  }
  last_pt.x = gi.ptsLocation.x;
  last_pt.y = gi.ptsLocation.y;
  *start = start_pt;
  return true;
}

WebKit::WebMouseWheelEvent MakeFakeScrollWheelEvent(HWND hwnd,
                                                    POINT start,
                                                    POINT delta) {
    WebKit::WebMouseWheelEvent result;
    result.type = WebInputEvent::MouseWheel;
    result.timeStampSeconds = ::GetMessageTime() / 1000.0;
    result.button = WebMouseEvent::ButtonNone;
    result.globalX = start.x;
    result.globalY = start.y;
    // Map to window coordinates.
    POINT client_point = { result.globalX, result.globalY };
    MapWindowPoints(0, hwnd, &client_point, 1);
    result.x = client_point.x;
    result.y = client_point.y;
    result.windowX = result.x;
    result.windowY = result.y;
    // Note that we support diagonal scrolling.
    result.deltaX = static_cast<float>(delta.x);
    result.wheelTicksX = WHEEL_DELTA;
    result.deltaY = static_cast<float>(delta.y);
    result.wheelTicksY = WHEEL_DELTA;
    return result;
}

static const int kTouchMask = 0x7;

inline int GetTouchType(const TOUCHINPUT& point) {
  return point.dwFlags & kTouchMask;
}

inline void SetTouchType(TOUCHINPUT* point, int type) {
  point->dwFlags = (point->dwFlags & kTouchMask) | type;
}

}  // namespace

///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewWin, public:

RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* 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),
      weak_factory_(this),
      parent_hwnd_(NULL),
      is_loading_(false),
      text_input_type_(ui::TEXT_INPUT_TYPE_NONE),
      is_fullscreen_(false),
      ignore_mouse_movement_(true),
      composition_range_(ui::Range::InvalidRange()),
      touch_state_(this),
      pointer_down_context_(false),
      focus_on_editable_field_(false),
      received_focus_change_after_pointer_down_(false),
      transparent_region_(0) {
  render_widget_host_ = static_cast<RenderWidgetHostImpl*>(widget);
  render_widget_host_->SetView(this);
  registrar_.Add(this,
                 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
                 content::NotificationService::AllBrowserContextsAndSources());
  registrar_.Add(this,
                 content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
                 content::NotificationService::AllBrowserContextsAndSources());
}

RenderWidgetHostViewWin::~RenderWidgetHostViewWin() {
  UnlockMouse();
  ResetTooltip();
}

void RenderWidgetHostViewWin::CreateWnd(HWND parent) {
  // ATL function to create the window.
  Create(parent);
  if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
    // MSDN recommends using the ITextInputPanel interface to display the on
    // screen keyboard. This interface does not work predictably due to focus
    // and activation bugs. We use the deprecated IPenInputPanel interface for
    // now which works reliably with the caveat being that the keyboard cannot
    // be closed via the system close menu. Esc or clicking elsewhere works.
    // TODO(ananta): Revisit this.
    virtual_keyboard_.CreateInstance(CLSID_PenInputPanel, NULL, CLSCTX_INPROC);
    if (virtual_keyboard_) {
      virtual_keyboard_->put_AttachedEditWindow(
          reinterpret_cast<int>(m_hWnd));
      virtual_keyboard_->put_DefaultPanel(PT_Keyboard);
      virtual_keyboard_->put_Visible(VARIANT_FALSE);
      virtual_keyboard_->put_AutoShow(VARIANT_FALSE);
    } else {
      NOTREACHED() << "Failed to create instance of pen input panel";
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
// RenderWidgetHostViewWin, RenderWidgetHostView implementation:

void RenderWidgetHostViewWin::InitAsChild(
    gfx::NativeView parent_view) {
  parent_hwnd_ = parent_view;
  CreateWnd(parent_view);
}

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_| may be NULL if the TabContents is in the process of
  // closing.
  if (render_widget_host_)
    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.
  if (render_widget_host_)
    render_widget_host_->WasHidden();
}

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);
}

gfx::NativeViewAccessible
RenderWidgetHostViewWin::GetNativeViewAccessible() {
  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 (!GetBrowserAccessibilityManager()) {
    // Return busy document tree while renderer accessibility tree loads.
    WebAccessibility::State busy_state =
        static_cast<WebAccessibility::State>(1 << WebAccessibility::STATE_BUSY);
    SetBrowserAccessibilityManager(
        BrowserAccessibilityManager::CreateEmptyDocument(
            m_hWnd, busy_state, this));
  }

  return GetBrowserAccessibilityManager()->GetRoot()->
      toBrowserAccessibilityWin();
}

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
    ChangeWindowMessageFilterEx(parent, WM_MOUSEWHEEL, MSGFLT_ALLOW, NULL);
    ChangeWindowMessageFilterEx(parent, WM_GESTURE, MSGFLT_ALLOW, NULL);
    ChangeWindowMessageFilterEx(parent, WM_APPCOMMAND, MSGFLT_ALLOW, NULL);
    ::RemovePropW(orig_parent, webkit::npapi::kNativeWindowClassFilterProp);
  }
  ::SetParent(window, parent);
  // How many times we try to find a PluginProcessHost whose process matches
  // the HWND.
  static const int kMaxTries = 5;
  BrowserThread::PostTask(
      BrowserThread::IO,
      FROM_HERE,
      base::Bind(&NotifyPluginProcessHostHelper, window, parent, kMaxTries));
  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,
      base::Bind(base::IgnoreResult(&::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() {
  NOTREACHED();
}

bool RenderWidgetHostViewWin::HasFocus() const {
  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) {
    // 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::TextInputStateChanged(
    ui::TextInputType type,
    bool can_compose_inline) {
  // 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);
  }
}

void RenderWidgetHostViewWin::SelectionBoundsChanged(
    const gfx::Rect& start_rect,
    const gfx::Rect& end_rect) {
  bool is_enabled = (text_input_type_ != ui::TEXT_INPUT_TYPE_NONE &&
      text_input_type_ != ui::TEXT_INPUT_TYPE_PASSWORD);
  // Only update caret position if the input method is enabled.
  if (is_enabled)
    ime_input_.UpdateCaretRect(m_hWnd, start_rect.Union(end_rect));
}

void RenderWidgetHostViewWin::ImeCancelComposition() {
  ime_input_.CancelIME(m_hWnd);
}

void RenderWidgetHostViewWin::ImeCompositionRangeChanged(
    const ui::Range& range) {
  composition_range_ = range;
}

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(&copy_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) {
  UpdateCursorIfOverSelf();
  Destroy();
}

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 string16& 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 string16 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) {
  content::RenderWidgetHostViewBase::SetBackground(background);
  render_widget_host_->SetBackground(background);
}

void RenderWidgetHostViewWin::UnhandledWheelEvent(
    const WebKit::WebMouseWheelEvent& event) {
}

void RenderWidgetHostViewWin::ProcessTouchAck(bool processed) {
}

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(ui::SetWindowSupportsRerouteMouseWheel(m_hWnd));

  if (base::win::GetVersion() >= base::win::VERSION_WIN7) {
    // Use gestures if touch event switch isn't present or registration fails.
    if (!CommandLine::ForCurrentProcess()->HasSwitch(
            switches::kEnableTouchEvents) ||
        !RegisterTouchWindow(m_hWnd, TWF_WANTPALM)) {
      // Single finger panning is consistent with other windows applications.
      const DWORD gesture_allow = GC_PAN_WITH_SINGLE_FINGER_VERTICALLY |
                                  GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY;
      const DWORD gesture_block = GC_PAN_WITH_GUTTER;
      GESTURECONFIG gc[] = {
          { GID_ZOOM, GC_ZOOM, 0 },
          { GID_PAN, gesture_allow , gesture_block},
          { GID_TWOFINGERTAP, GC_TWOFINGERTAP , 0},
          { GID_PRESSANDTAP, GC_PRESSANDTAP , 0}
      };
      if (!SetGestureConfig(m_hWnd, 0, arraysize(gc), gc,
          sizeof(GESTURECONFIG))) {
        NOTREACHED();
      }
    }
  }
  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();

  if (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
      IsTouchWindow(m_hWnd, NULL)) {
    UnregisterTouchWindow(m_hWnd);
  }

  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;
    RGNDATA* region_data = NULL;
    RECT* region_rects = NULL;

    if (data_size) {
      region_data_buf.reset(new char[data_size]);
      region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get());
      region_rects = reinterpret_cast<RECT*>(region_data->Buffer);
      data_size = GetRegionData(damage_region, data_size, region_data);
    }

    if (!data_size) {
      // Grabbing the damaged regions failed, fake with the whole rect.
      data_size = sizeof(RGNDATAHEADER) + sizeof(RECT);
      region_data_buf.reset(new char[data_size]);
      region_data = reinterpret_cast<RGNDATA*>(region_data_buf.get());
      region_rects = reinterpret_cast<RECT*>(region_data->Buffer);
      region_data->rdh.nCount = 1;
      region_rects[0] = damaged_rect.ToRECT();
    }

    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()) {
        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::Rect dirty_area(dirty_rect);
    gfx::CanvasSkia canvas(dirty_area.size(), true);
    canvas.Translate(gfx::Point().Subtract(dirty_area.origin()));

    gfx::Rect dc_rect(dc->m_ps.rcPaint);
    canvas.TileImageInt(background_, 0, 0, dc_rect.width(), dc_rect.height());

    skia::DrawToNativeContext(canvas.sk_canvas(), *dc, dirty_area.x(),
                              dirty_area.y(), 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.
}

void RenderWidgetHostViewWin::SetClickthroughRegion(SkRegion* region) {
  transparent_region_.Set(region);
{'fraction_non_alphanumeric': 0.10600377475691043, 'fraction_numerical': 0.002866903982607449, 'mean_word_length': 3.603519384107781, 'pattern_counts': {'":': 4, '<': 121, '<?xml version=': 0, '>': 175, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 52, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5829733', 'n_tokens_mistral': 26435, 'n_tokens_neox': 25910, 'n_words': 6468}