OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/web_contents/web_contents_view_win.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/memory/scoped_vector.h" | |
9 #include "content/browser/frame_host/interstitial_page_impl.h" | |
10 #include "content/browser/renderer_host/render_view_host_factory.h" | |
11 #include "content/browser/renderer_host/render_view_host_impl.h" | |
12 #include "content/browser/renderer_host/render_widget_host_view_win.h" | |
13 #include "content/browser/web_contents/web_contents_drag_win.h" | |
14 #include "content/browser/web_contents/web_contents_impl.h" | |
15 #include "content/browser/web_contents/web_drag_dest_win.h" | |
16 #include "content/public/browser/web_contents_delegate.h" | |
17 #include "content/public/browser/web_contents_view_delegate.h" | |
18 #include "ui/base/win/hidden_window.h" | |
19 #include "ui/base/win/hwnd_subclass.h" | |
20 #include "ui/gfx/screen.h" | |
21 | |
22 namespace content { | |
23 WebContentsViewPort* CreateWebContentsView( | |
24 WebContentsImpl* web_contents, | |
25 WebContentsViewDelegate* delegate, | |
26 RenderViewHostDelegateView** render_view_host_delegate_view) { | |
27 WebContentsViewWin* rv = new WebContentsViewWin(web_contents, delegate); | |
28 *render_view_host_delegate_view = rv; | |
29 return rv; | |
30 } | |
31 | |
32 namespace { | |
33 | |
34 typedef std::map<HWND, WebContentsViewWin*> HwndToWcvMap; | |
35 HwndToWcvMap hwnd_to_wcv_map; | |
36 | |
37 void RemoveHwndToWcvMapEntry(WebContentsViewWin* wcv) { | |
38 HwndToWcvMap::iterator it; | |
39 for (it = hwnd_to_wcv_map.begin(); it != hwnd_to_wcv_map.end();) { | |
40 if (it->second == wcv) | |
41 hwnd_to_wcv_map.erase(it++); | |
42 else | |
43 ++it; | |
44 } | |
45 } | |
46 | |
47 BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam) { | |
48 HwndToWcvMap::iterator it = hwnd_to_wcv_map.find(hwnd); | |
49 if (it == hwnd_to_wcv_map.end()) | |
50 return TRUE; // must return TRUE to continue enumeration. | |
51 WebContentsViewWin* wcv = it->second; | |
52 RenderWidgetHostViewWin* rwhv = static_cast<RenderWidgetHostViewWin*>( | |
53 wcv->web_contents()->GetRenderWidgetHostView()); | |
54 if (rwhv) | |
55 rwhv->UpdateScreenInfo(rwhv->GetNativeView()); | |
56 | |
57 return TRUE; // must return TRUE to continue enumeration. | |
58 } | |
59 | |
60 class PositionChangedMessageFilter : public ui::HWNDMessageFilter { | |
61 public: | |
62 PositionChangedMessageFilter() {} | |
63 | |
64 private: | |
65 // Overridden from ui::HWNDMessageFilter: | |
66 virtual bool FilterMessage(HWND hwnd, | |
67 UINT message, | |
68 WPARAM w_param, | |
69 LPARAM l_param, | |
70 LRESULT* l_result) OVERRIDE { | |
71 if (message == WM_WINDOWPOSCHANGED || message == WM_SETTINGCHANGE) | |
72 EnumChildWindows(hwnd, EnumChildProc, 0); | |
73 | |
74 return false; | |
75 } | |
76 | |
77 DISALLOW_COPY_AND_ASSIGN(PositionChangedMessageFilter); | |
78 }; | |
79 | |
80 void AddFilterToParentHwndSubclass(HWND hwnd, ui::HWNDMessageFilter* filter) { | |
81 HWND parent = ::GetAncestor(hwnd, GA_ROOT); | |
82 if (parent) { | |
83 ui::HWNDSubclass::RemoveFilterFromAllTargets(filter); | |
84 ui::HWNDSubclass::AddFilterToTarget(parent, filter); | |
85 } | |
86 } | |
87 | |
88 } // namespace namespace | |
89 | |
90 WebContentsViewWin::WebContentsViewWin(WebContentsImpl* web_contents, | |
91 WebContentsViewDelegate* delegate) | |
92 : web_contents_(web_contents), | |
93 delegate_(delegate), | |
94 hwnd_message_filter_(new PositionChangedMessageFilter) { | |
95 } | |
96 | |
97 WebContentsViewWin::~WebContentsViewWin() { | |
98 RemoveHwndToWcvMapEntry(this); | |
99 | |
100 if (IsWindow(hwnd())) | |
101 DestroyWindow(hwnd()); | |
102 } | |
103 | |
104 gfx::NativeView WebContentsViewWin::GetNativeView() const { | |
105 return hwnd(); | |
106 } | |
107 | |
108 gfx::NativeView WebContentsViewWin::GetContentNativeView() const { | |
109 RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView(); | |
110 return rwhv ? rwhv->GetNativeView() : NULL; | |
111 } | |
112 | |
113 gfx::NativeWindow WebContentsViewWin::GetTopLevelNativeWindow() const { | |
114 return ::GetAncestor(GetNativeView(), GA_ROOT); | |
115 } | |
116 | |
117 void WebContentsViewWin::GetContainerBounds(gfx::Rect *out) const { | |
118 // Copied from NativeWidgetWin::GetClientAreaScreenBounds(). | |
119 RECT r; | |
120 GetClientRect(hwnd(), &r); | |
121 POINT point = { r.left, r.top }; | |
122 ClientToScreen(hwnd(), &point); | |
123 *out = gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top); | |
124 } | |
125 | |
126 void WebContentsViewWin::OnTabCrashed(base::TerminationStatus status, | |
127 int error_code) { | |
128 } | |
129 | |
130 void WebContentsViewWin::SizeContents(const gfx::Size& size) { | |
131 gfx::Rect bounds; | |
132 GetContainerBounds(&bounds); | |
133 if (bounds.size() != size) { | |
134 SetWindowPos(hwnd(), NULL, 0, 0, size.width(), size.height(), | |
135 SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); | |
136 } else { | |
137 // Our size matches what we want but the renderers size may not match. | |
138 // Pretend we were resized so that the renderers size is updated too. | |
139 if (web_contents_->GetInterstitialPage()) | |
140 web_contents_->GetInterstitialPage()->SetSize(size); | |
141 RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView(); | |
142 if (rwhv) | |
143 rwhv->SetSize(size); | |
144 } | |
145 } | |
146 | |
147 void WebContentsViewWin::CreateView( | |
148 const gfx::Size& initial_size, gfx::NativeView context) { | |
149 initial_size_ = initial_size; | |
150 | |
151 set_window_style(WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); | |
152 | |
153 Init(ui::GetHiddenWindow(), gfx::Rect(initial_size_)); | |
154 | |
155 // Remove the root view drop target so we can register our own. | |
156 RevokeDragDrop(GetNativeView()); | |
157 drag_dest_ = new WebDragDest(hwnd(), web_contents_); | |
158 if (delegate_) { | |
159 WebDragDestDelegate* delegate = delegate_->GetDragDestDelegate(); | |
160 if (delegate) | |
161 drag_dest_->set_delegate(delegate); | |
162 } | |
163 } | |
164 | |
165 void WebContentsViewWin::Focus() { | |
166 if (web_contents_->GetInterstitialPage()) { | |
167 web_contents_->GetInterstitialPage()->Focus(); | |
168 return; | |
169 } | |
170 | |
171 if (delegate_.get() && delegate_->Focus()) | |
172 return; | |
173 | |
174 RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView(); | |
175 if (rwhv) | |
176 rwhv->Focus(); | |
177 } | |
178 | |
179 void WebContentsViewWin::SetInitialFocus() { | |
180 if (web_contents_->FocusLocationBarByDefault()) | |
181 web_contents_->SetFocusToLocationBar(false); | |
182 else | |
183 Focus(); | |
184 } | |
185 | |
186 void WebContentsViewWin::StoreFocus() { | |
187 if (delegate_) | |
188 delegate_->StoreFocus(); | |
189 } | |
190 | |
191 void WebContentsViewWin::RestoreFocus() { | |
192 if (delegate_) | |
193 delegate_->RestoreFocus(); | |
194 } | |
195 | |
196 DropData* WebContentsViewWin::GetDropData() const { | |
197 return drag_dest_->current_drop_data(); | |
198 } | |
199 | |
200 gfx::Rect WebContentsViewWin::GetViewBounds() const { | |
201 RECT r; | |
202 GetWindowRect(hwnd(), &r); | |
203 return gfx::Rect(r); | |
204 } | |
205 | |
206 RenderWidgetHostView* WebContentsViewWin::CreateViewForWidget( | |
207 RenderWidgetHost* render_widget_host) { | |
208 if (render_widget_host->GetView()) { | |
209 // During testing, the view will already be set up in most cases to the | |
210 // test view, so we don't want to clobber it with a real one. To verify that | |
211 // this actually is happening (and somebody isn't accidentally creating the | |
212 // view twice), we check for the RVH Factory, which will be set when we're | |
213 // making special ones (which go along with the special views). | |
214 DCHECK(RenderViewHostFactory::has_factory()); | |
215 return render_widget_host->GetView(); | |
216 } | |
217 | |
218 RenderWidgetHostViewWin* view = static_cast<RenderWidgetHostViewWin*>( | |
219 RenderWidgetHostView::CreateViewForWidget(render_widget_host)); | |
220 view->CreateWnd(GetNativeView()); | |
221 view->ShowWindow(SW_SHOW); | |
222 view->SetSize(initial_size_); | |
223 return view; | |
224 } | |
225 | |
226 RenderWidgetHostView* WebContentsViewWin::CreateViewForPopupWidget( | |
227 RenderWidgetHost* render_widget_host) { | |
228 return RenderWidgetHostViewPort::CreateViewForWidget(render_widget_host); | |
229 } | |
230 | |
231 void WebContentsViewWin::SetPageTitle(const base::string16& title) { | |
232 // It's possible to get this after the hwnd has been destroyed. | |
233 if (GetNativeView()) | |
234 ::SetWindowText(GetNativeView(), title.c_str()); | |
235 } | |
236 | |
237 void WebContentsViewWin::RenderViewCreated(RenderViewHost* host) { | |
238 } | |
239 | |
240 void WebContentsViewWin::RenderViewSwappedIn(RenderViewHost* host) { | |
241 } | |
242 | |
243 void WebContentsViewWin::SetOverscrollControllerEnabled(bool enabled) { | |
244 } | |
245 | |
246 void WebContentsViewWin::ShowContextMenu(const ContextMenuParams& params) { | |
247 if (delegate_) | |
248 delegate_->ShowContextMenu(params); | |
249 // WARNING: this may have been deleted. | |
250 } | |
251 | |
252 void WebContentsViewWin::ShowPopupMenu(const gfx::Rect& bounds, | |
253 int item_height, | |
254 double item_font_size, | |
255 int selected_item, | |
256 const std::vector<MenuItem>& items, | |
257 bool right_aligned, | |
258 bool allow_multiple_selection) { | |
259 // External popup menus are only used on Mac and Android. | |
260 NOTIMPLEMENTED(); | |
261 } | |
262 | |
263 void WebContentsViewWin::StartDragging(const DropData& drop_data, | |
264 blink::WebDragOperationsMask operations, | |
265 const gfx::ImageSkia& image, | |
266 const gfx::Vector2d& image_offset, | |
267 const DragEventSourceInfo& event_info) { | |
268 drag_handler_ = new WebContentsDragWin( | |
269 GetNativeView(), | |
270 web_contents_, | |
271 drag_dest_, | |
272 base::Bind(&WebContentsViewWin::EndDragging, base::Unretained(this))); | |
273 drag_handler_->StartDragging(drop_data, operations, image, image_offset); | |
274 } | |
275 | |
276 void WebContentsViewWin::UpdateDragCursor(blink::WebDragOperation operation) { | |
277 drag_dest_->set_drag_cursor(operation); | |
278 } | |
279 | |
280 void WebContentsViewWin::GotFocus() { | |
281 if (web_contents_->GetDelegate()) | |
282 web_contents_->GetDelegate()->WebContentsFocused(web_contents_); | |
283 } | |
284 | |
285 void WebContentsViewWin::TakeFocus(bool reverse) { | |
286 if (web_contents_->GetDelegate() && | |
287 !web_contents_->GetDelegate()->TakeFocus(web_contents_, reverse) && | |
288 delegate_.get()) { | |
289 delegate_->TakeFocus(reverse); | |
290 } | |
291 } | |
292 | |
293 void WebContentsViewWin::EndDragging() { | |
294 drag_handler_ = NULL; | |
295 web_contents_->SystemDragEnded(); | |
296 } | |
297 | |
298 void WebContentsViewWin::CloseTab() { | |
299 RenderViewHost* rvh = web_contents_->GetRenderViewHost(); | |
300 rvh->GetDelegate()->Close(rvh); | |
301 } | |
302 | |
303 LRESULT WebContentsViewWin::OnCreate( | |
304 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
305 hwnd_to_wcv_map.insert(std::make_pair(hwnd(), this)); | |
306 AddFilterToParentHwndSubclass(hwnd(), hwnd_message_filter_.get()); | |
307 return 0; | |
308 } | |
309 | |
310 LRESULT WebContentsViewWin::OnDestroy( | |
311 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
312 if (drag_dest_) { | |
313 RevokeDragDrop(GetNativeView()); | |
314 drag_dest_ = NULL; | |
315 } | |
316 if (drag_handler_) { | |
317 drag_handler_->CancelDrag(); | |
318 drag_handler_ = NULL; | |
319 } | |
320 return 0; | |
321 } | |
322 | |
323 LRESULT WebContentsViewWin::OnWindowPosChanged( | |
324 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
325 | |
326 // Our parent might have changed. So we re-install our hwnd message filter. | |
327 AddFilterToParentHwndSubclass(hwnd(), hwnd_message_filter_.get()); | |
328 | |
329 WINDOWPOS* window_pos = reinterpret_cast<WINDOWPOS*>(lparam); | |
330 if (window_pos->flags & SWP_HIDEWINDOW) { | |
331 web_contents_->WasHidden(); | |
332 return 0; | |
333 } | |
334 | |
335 // The WebContents was shown by a means other than the user selecting a | |
336 // Tab, e.g. the window was minimized then restored. | |
337 if (window_pos->flags & SWP_SHOWWINDOW) | |
338 web_contents_->WasShown(); | |
339 | |
340 RenderWidgetHostView* rwhv = web_contents_->GetRenderWidgetHostView(); | |
341 if (rwhv) { | |
342 RenderWidgetHostViewWin* view = static_cast<RenderWidgetHostViewWin*>(rwhv); | |
343 view->UpdateScreenInfo(view->GetNativeView()); | |
344 } | |
345 | |
346 // Unless we were specifically told not to size, cause the renderer to be | |
347 // sized to the new bounds, which forces a repaint. Not required for the | |
348 // simple minimize-restore case described above, for example, since the | |
349 // size hasn't changed. | |
350 if (window_pos->flags & SWP_NOSIZE) | |
351 return 0; | |
352 | |
353 gfx::Size size(window_pos->cx, window_pos->cy); | |
354 if (web_contents_->GetInterstitialPage()) | |
355 web_contents_->GetInterstitialPage()->SetSize(size); | |
356 if (rwhv) | |
357 rwhv->SetSize(size); | |
358 | |
359 if (delegate_) | |
360 delegate_->SizeChanged(size); | |
361 | |
362 return 0; | |
363 } | |
364 | |
365 LRESULT WebContentsViewWin::OnMouseDown( | |
366 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
367 // Make sure this WebContents is activated when it is clicked on. | |
368 if (web_contents_->GetDelegate()) | |
369 web_contents_->GetDelegate()->ActivateContents(web_contents_); | |
370 return 0; | |
371 } | |
372 | |
373 LRESULT WebContentsViewWin::OnMouseMove( | |
374 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
375 // Let our delegate know that the mouse moved (useful for resetting status | |
376 // bubble state). | |
377 if (web_contents_->GetDelegate()) { | |
378 web_contents_->GetDelegate()->ContentsMouseEvent( | |
379 web_contents_, | |
380 gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(), | |
381 true); | |
382 } | |
383 return 0; | |
384 } | |
385 | |
386 LRESULT WebContentsViewWin::OnNCCalcSize( | |
387 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
388 // Hack for ThinkPad mouse wheel driver. We have set the fake scroll bars | |
389 // to receive scroll messages from ThinkPad touch-pad driver. Suppress | |
390 // painting of scrollbars by returning 0 size for them. | |
391 return 0; | |
392 } | |
393 | |
394 LRESULT WebContentsViewWin::OnNCHitTest( | |
395 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
396 return HTTRANSPARENT; | |
397 } | |
398 | |
399 LRESULT WebContentsViewWin::OnScroll( | |
400 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
401 int scroll_type = LOWORD(wparam); | |
402 short position = HIWORD(wparam); | |
403 HWND scrollbar = reinterpret_cast<HWND>(lparam); | |
404 // This window can receive scroll events as a result of the ThinkPad's | |
405 // touch-pad scroll wheel emulation. | |
406 // If ctrl is held, zoom the UI. There are three issues with this: | |
407 // 1) Should the event be eaten or forwarded to content? We eat the event, | |
408 // which is like Firefox and unlike IE. | |
409 // 2) Should wheel up zoom in or out? We zoom in (increase font size), which | |
410 // is like IE and Google maps, but unlike Firefox. | |
411 // 3) Should the mouse have to be over the content area? We zoom as long as | |
412 // content has focus, although FF and IE require that the mouse is over | |
413 // content. This is because all events get forwarded when content has | |
414 // focus. | |
415 if (GetAsyncKeyState(VK_CONTROL) & 0x8000) { | |
416 int distance = 0; | |
417 switch (scroll_type) { | |
418 case SB_LINEUP: | |
419 distance = WHEEL_DELTA; | |
420 break; | |
421 case SB_LINEDOWN: | |
422 distance = -WHEEL_DELTA; | |
423 break; | |
424 // TODO(joshia): Handle SB_PAGEUP, SB_PAGEDOWN, SB_THUMBPOSITION, | |
425 // and SB_THUMBTRACK for completeness | |
426 default: | |
427 break; | |
428 } | |
429 | |
430 web_contents_->GetDelegate()->ContentsZoomChange(distance > 0); | |
431 return 0; | |
432 } | |
433 | |
434 // Reflect scroll message to the view() to give it a chance | |
435 // to process scrolling. | |
436 SendMessage(GetContentNativeView(), message, wparam, lparam); | |
437 return 0; | |
438 } | |
439 | |
440 LRESULT WebContentsViewWin::OnSize( | |
441 UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { | |
442 // NOTE: Because we handle OnWindowPosChanged without calling DefWindowProc, | |
443 // OnSize is NOT called on window resize. This handler is called only once | |
444 // when the window is created. | |
445 // Don't call base class OnSize to avoid useless layout for 0x0 size. | |
446 // We will get OnWindowPosChanged later and layout root view in WasSized. | |
447 | |
448 // Hack for ThinkPad touch-pad driver. | |
449 // Set fake scrollbars so that we can get scroll messages, | |
450 SCROLLINFO si = {0}; | |
451 si.cbSize = sizeof(si); | |
452 si.fMask = SIF_ALL; | |
453 | |
454 si.nMin = 1; | |
455 si.nMax = 100; | |
456 si.nPage = 10; | |
457 si.nPos = 50; | |
458 | |
459 ::SetScrollInfo(hwnd(), SB_HORZ, &si, FALSE); | |
460 ::SetScrollInfo(hwnd(), SB_VERT, &si, FALSE); | |
461 | |
462 return 1; | |
463 } | |
464 | |
465 } // namespace content | |
OLD | NEW |