Index: content/browser/renderer_host/render_widget_host_view_base.cc |
=================================================================== |
--- content/browser/renderer_host/render_widget_host_view_base.cc (revision 156019) |
+++ content/browser/renderer_host/render_widget_host_view_base.cc (working copy) |
@@ -12,6 +12,21 @@ |
#include "ui/gfx/display.h" |
#include "ui/gfx/screen.h" |
+#if defined(OS_WIN) |
+#include "base/command_line.h" |
+#include "base/message_loop.h" |
+#include "base/win/wrapped_window_proc.h" |
+#include "content/browser/plugin_process_host.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "content/public/browser/child_process_data.h" |
+#include "content/public/common/content_switches.h" |
+#include "ui/base/win/hwnd_util.h" |
+#include "ui/gfx/gdi_util.h" |
+#include "webkit/plugins/npapi/plugin_constants_win.h" |
+#include "webkit/plugins/npapi/webplugin.h" |
+#include "webkit/plugins/npapi/webplugin_delegate_impl.h" |
+#endif |
+ |
#if defined(TOOLKIT_GTK) |
#include <gdk/gdkx.h> |
#include <gtk/gtk.h> |
@@ -39,6 +54,260 @@ |
return FromRWHV(RenderWidgetHostView::CreateViewForWidget(widget)); |
} |
+#if defined(OS_WIN) |
+ |
+namespace { |
+ |
+// |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), |
+ base::TimeDelta::FromMilliseconds(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); |
+} |
+ |
+// 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); |
+} |
+ |
+// Create an intermediate window between the given HWND and its parent. |
+HWND ReparentWindow(HWND window) { |
+ static ATOM atom = 0; |
+ static HMODULE instance = NULL; |
+ if (!atom) { |
+ WNDCLASSEX window_class; |
+ base::win::InitializeWindowClass( |
+ webkit::npapi::kWrapperNativeWindowClassName, |
+ &base::win::WrappedWindowProc<PluginWrapperWindowProc>, |
+ CS_DBLCLKS, |
+ 0, |
+ 0, |
+ NULL, |
+ // xxx reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), |
+ reinterpret_cast<HBRUSH>(COLOR_GRAYTEXT+1), |
+ NULL, |
+ NULL, |
+ NULL, |
+ &window_class); |
+ instance = window_class.hInstance; |
+ atom = RegisterClassEx(&window_class); |
+ } |
+ DCHECK(atom); |
+ |
+ HWND orig_parent = ::GetParent(window); |
+ HWND parent = CreateWindowEx( |
+ WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, |
+ MAKEINTATOM(atom), 0, |
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, |
+ 0, 0, 0, 0, orig_parent, 0, instance, 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; |
+} |
+ |
+BOOL CALLBACK PainEnumChildProc(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; |
+} |
+ |
+} // namespace |
+ |
+// static |
+void RenderWidgetHostViewBase::MovePluginWindowsHelper( |
+ HWND parent, |
+ const std::vector<webkit::npapi::WebPluginGeometry>& moves) { |
+ if (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>(moves.size())); |
+ |
+ if (!defer_window_pos_info) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ for (size_t i = 0; i < moves.size(); ++i) { |
+ unsigned long flags = 0; |
+ const webkit::npapi::WebPluginGeometry& move = 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 cur_parent = ::GetParent(window); |
+ if (!::IsWindow(window)) |
+ continue; |
+ |
+ if (oop_plugins) { |
+ if (cur_parent == parent) { |
+ // 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. |
+ cur_parent = ReparentWindow(window); |
+ ::ShowWindow(window, SW_SHOW); // Window was created hidden. |
+ } else if (::GetParent(cur_parent) != parent) { |
+ // 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 = cur_parent; |
+ } |
+ |
+ if (move.visible) |
+ flags |= SWP_SHOWWINDOW; |
+ else |
+ flags |= SWP_HIDEWINDOW; |
+ |
+#if defined(USE_AURA) |
+ // Without this flag, Windows repaints the parent area uncovered by this |
+ // move. However it only looks at the plugin rectangle and ignores the |
+ // clipping region. In Aura, the browser chrome could be under the plugin, |
+ // and if Windows tries to paint it synchronously inside EndDeferWindowsPos |
+ // then it won't have the data and it will flash white. So instead we |
+ // manually redraw the plugin. |
+ // Why not do this for native Windows? Not sure if there are any performance |
+ // issues with this. |
+ flags |= SWP_NOREDRAW; |
+#endif |
+ |
+ 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); |
+ |
+#if defined(USE_AURA) |
+ for (size_t i = 0; i < moves.size(); ++i) { |
+ const webkit::npapi::WebPluginGeometry& move = moves[i]; |
+ RECT r; |
+ GetWindowRect(move.window, &r); |
+ gfx::Rect gr(r); |
+ PainEnumChildProc(move.window, reinterpret_cast<LPARAM>(&gr)); |
+ } |
+#endif |
+} |
+ |
+// static |
+void RenderWidgetHostViewBase::PaintPluginWindowsHelper( |
+ HWND parent, const gfx::Rect& damaged_screen_rect) { |
+ LPARAM lparam = reinterpret_cast<LPARAM>(&damaged_screen_rect); |
+ EnumChildWindows(parent, PainEnumChildProc, lparam); |
+} |
+ |
+#endif // OS_WIN |
+ |
RenderWidgetHostViewBase::RenderWidgetHostViewBase() |
: popup_type_(WebKit::WebPopupTypeNone), |
mouse_locked_(false), |