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/renderer_host/render_widget_host_view_base.h" 6 7 #include "base/logging.h" 8 #include "content/browser/accessibility/browser_accessibility_manager.h" 9 #include "content/browser/gpu/gpu_data_manager_impl.h" 10 #include "content/browser/renderer_host/input/synthetic_gesture_target_base.h" 11 #include "content/browser/renderer_host/render_process_host_impl.h" 12 #include "content/browser/renderer_host/render_widget_host_impl.h" 13 #include "content/common/content_switches_internal.h" 14 #include "content/public/browser/render_widget_host_view_frame_subscriber.h" 15 #include "ui/gfx/display.h" 16 #include "ui/gfx/screen.h" 17 #include "ui/gfx/size_conversions.h" 18 #include "ui/gfx/size_f.h" 19 20 #if defined(OS_WIN) 21 #include "base/command_line.h" 22 #include "base/message_loop/message_loop.h" 23 #include "base/win/wrapped_window_proc.h" 24 #include "content/browser/plugin_process_host.h" 25 #include "content/browser/plugin_service_impl.h" 26 #include "content/common/plugin_constants_win.h" 27 #include "content/common/webplugin_geometry.h" 28 #include "content/public/browser/browser_thread.h" 29 #include "content/public/browser/child_process_data.h" 30 #include "content/public/common/content_switches.h" 31 #include "ui/gfx/gdi_util.h" 32 #include "ui/gfx/win/dpi.h" 33 #include "ui/gfx/win/hwnd_util.h" 34 #endif 35 36 namespace content { 37 38 #if defined(OS_WIN) 39 40 namespace { 41 42 // |window| is the plugin HWND, created and destroyed in the plugin process. 43 // |parent| is the parent HWND, created and destroyed on the browser UI thread. 44 void NotifyPluginProcessHostHelper(HWND window, HWND parent, int tries) { 45 // How long to wait between each try. 46 static const int kTryDelayMs = 200; 47 48 DWORD plugin_process_id; 49 bool found_starting_plugin_process = false; 50 GetWindowThreadProcessId(window, &plugin_process_id); 51 for (PluginProcessHostIterator iter; !iter.Done(); ++iter) { 52 if (!iter.GetData().handle) { 53 found_starting_plugin_process = true; 54 continue; 55 } 56 if (base::GetProcId(iter.GetData().handle) == plugin_process_id) { 57 iter->AddWindow(parent); 58 return; 59 } 60 } 61 62 if (found_starting_plugin_process) { 63 // A plugin process has started but we don't have its handle yet. Since 64 // it's most likely the one for this plugin, try a few more times after a 65 // delay. 66 if (tries > 0) { 67 base::MessageLoop::current()->PostDelayedTask( 68 FROM_HERE, 69 base::Bind(&NotifyPluginProcessHostHelper, window, parent, tries - 1), 70 base::TimeDelta::FromMilliseconds(kTryDelayMs)); 71 return; 72 } 73 } 74 75 // The plugin process might have died in the time to execute the task, don't 76 // leak the HWND. 77 PostMessage(parent, WM_CLOSE, 0, 0); 78 } 79 80 // The plugin wrapper window which lives in the browser process has this proc 81 // as its window procedure. We only handle the WM_PARENTNOTIFY message sent by 82 // windowed plugins for mouse input. This is forwarded off to the wrappers 83 // parent which is typically the RVH window which turns on user gesture. 84 LRESULT CALLBACK PluginWrapperWindowProc(HWND window, unsigned int message, 85 WPARAM wparam, LPARAM lparam) { 86 if (message == WM_PARENTNOTIFY) { 87 switch (LOWORD(wparam)) { 88 case WM_LBUTTONDOWN: 89 case WM_RBUTTONDOWN: 90 case WM_MBUTTONDOWN: 91 ::SendMessage(GetParent(window), message, wparam, lparam); 92 return 0; 93 default: 94 break; 95 } 96 } 97 return ::DefWindowProc(window, message, wparam, lparam); 98 } 99 100 bool IsPluginWrapperWindow(HWND window) { 101 return gfx::GetClassNameW(window) == 102 base::string16(kWrapperNativeWindowClassName); 103 } 104 105 // Create an intermediate window between the given HWND and its parent. 106 HWND ReparentWindow(HWND window, HWND parent) { 107 static ATOM atom = 0; 108 static HMODULE instance = NULL; 109 if (!atom) { 110 WNDCLASSEX window_class; 111 base::win::InitializeWindowClass( 112 kWrapperNativeWindowClassName, 113 &base::win::WrappedWindowProc<PluginWrapperWindowProc>, 114 CS_DBLCLKS, 115 0, 116 0, 117 NULL, 118 // xxx reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), 119 reinterpret_cast<HBRUSH>(COLOR_GRAYTEXT+1), 120 NULL, 121 NULL, 122 NULL, 123 &window_class); 124 instance = window_class.hInstance; 125 atom = RegisterClassEx(&window_class); 126 } 127 DCHECK(atom); 128 129 HWND new_parent = CreateWindowEx( 130 WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, 131 MAKEINTATOM(atom), 0, 132 WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 133 0, 0, 0, 0, parent, 0, instance, 0); 134 gfx::CheckWindowCreated(new_parent); 135 ::SetParent(window, new_parent); 136 // How many times we try to find a PluginProcessHost whose process matches 137 // the HWND. 138 static const int kMaxTries = 5; 139 BrowserThread::PostTask( 140 BrowserThread::IO, 141 FROM_HERE, 142 base::Bind(&NotifyPluginProcessHostHelper, window, new_parent, 143 kMaxTries)); 144 return new_parent; 145 } 146 147 BOOL CALLBACK PaintEnumChildProc(HWND hwnd, LPARAM lparam) { 148 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(hwnd)) 149 return TRUE; 150 151 gfx::Rect* rect = reinterpret_cast<gfx::Rect*>(lparam); 152 gfx::Rect rect_in_pixels = gfx::win::DIPToScreenRect(*rect); 153 static UINT msg = RegisterWindowMessage(kPaintMessageName); 154 WPARAM wparam = MAKEWPARAM(rect_in_pixels.x(), rect_in_pixels.y()); 155 lparam = MAKELPARAM(rect_in_pixels.width(), rect_in_pixels.height()); 156 157 // SendMessage gets the message across much quicker than PostMessage, since it 158 // doesn't get queued. When the plugin thread calls PeekMessage or other 159 // Win32 APIs, sent messages are dispatched automatically. 160 SendNotifyMessage(hwnd, msg, wparam, lparam); 161 162 return TRUE; 163 } 164 165 // Windows callback for OnDestroy to detach the plugin windows. 166 BOOL CALLBACK DetachPluginWindowsCallbackInternal(HWND window, LPARAM param) { 167 RenderWidgetHostViewBase::DetachPluginWindowsCallback(window); 168 return TRUE; 169 } 170 171 } // namespace 172 173 // static 174 void RenderWidgetHostViewBase::DetachPluginWindowsCallback(HWND window) { 175 if (PluginServiceImpl::GetInstance()->IsPluginWindow(window) && 176 !IsHungAppWindow(window)) { 177 ::ShowWindow(window, SW_HIDE); 178 SetParent(window, NULL); 179 } 180 } 181 182 // static 183 void RenderWidgetHostViewBase::MovePluginWindowsHelper( 184 HWND parent, 185 const std::vector<WebPluginGeometry>& moves) { 186 if (moves.empty()) 187 return; 188 189 bool oop_plugins = 190 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); 191 192 HDWP defer_window_pos_info = 193 ::BeginDeferWindowPos(static_cast<int>(moves.size())); 194 195 if (!defer_window_pos_info) { 196 NOTREACHED(); 197 return; 198 } 199 200 #if defined(USE_AURA) 201 std::vector<RECT> invalidate_rects; 202 #endif 203 204 for (size_t i = 0; i < moves.size(); ++i) { 205 unsigned long flags = 0; 206 const WebPluginGeometry& move = moves[i]; 207 HWND window = move.window; 208 209 // As the plugin parent window which lives on the browser UI thread is 210 // destroyed asynchronously, it is possible that we have a stale window 211 // sent in by the renderer for moving around. 212 // Note: get the parent before checking if the window is valid, to avoid a 213 // race condition where the window is destroyed after the check but before 214 // the GetParent call. 215 HWND cur_parent = ::GetParent(window); 216 if (!::IsWindow(window)) 217 continue; 218 219 if (!PluginServiceImpl::GetInstance()->IsPluginWindow(window)) { 220 // The renderer should only be trying to move plugin windows. However, 221 // this may happen as a result of a race condition (i.e. even after the 222 // check right above), so we ignore it. 223 continue; 224 } 225 226 if (oop_plugins) { 227 if (cur_parent == GetDesktopWindow()) { 228 // The plugin window hasn't been parented yet, add an intermediate 229 // window that lives on this thread to speed up scrolling. Note this 230 // only works with out of process plugins since we depend on 231 // PluginProcessHost to destroy the intermediate HWNDs. 232 cur_parent = ReparentWindow(window, parent); 233 ::ShowWindow(window, SW_SHOW); // Window was created hidden. 234 } else if (!IsPluginWrapperWindow(cur_parent)) { 235 continue; // Race if plugin process is shutting down. 236 } 237 238 // We move the intermediate parent window which doesn't result in cross- 239 // process synchronous Windows messages. 240 window = cur_parent; 241 } else { 242 if (cur_parent == GetDesktopWindow()) 243 SetParent(window, parent); 244 } 245 246 if (move.visible) 247 flags |= SWP_SHOWWINDOW; 248 else 249 flags |= SWP_HIDEWINDOW; 250 251 #if defined(USE_AURA) 252 if (GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) { 253 // Without this flag, Windows repaints the parent area uncovered by this 254 // move. However when software compositing is used the clipping region is 255 // ignored. Since in Aura the browser chrome could be under the plugin, if 256 // if Windows tries to paint it synchronously inside EndDeferWindowsPos 257 // then it won't have the data and it will flash white. So instead we 258 // manually redraw the plugin. 259 // Why not do this for native Windows? Not sure if there are any 260 // performance issues with this. 261 flags |= SWP_NOREDRAW; 262 } 263 #endif 264 265 if (move.rects_valid) { 266 gfx::Rect clip_rect_in_pixel = gfx::win::DIPToScreenRect(move.clip_rect); 267 HRGN hrgn = ::CreateRectRgn(clip_rect_in_pixel.x(), 268 clip_rect_in_pixel.y(), 269 clip_rect_in_pixel.right(), 270 clip_rect_in_pixel.bottom()); 271 gfx::SubtractRectanglesFromRegion(hrgn, move.cutout_rects); 272 273 // Note: System will own the hrgn after we call SetWindowRgn, 274 // so we don't need to call DeleteObject(hrgn) 275 ::SetWindowRgn(window, hrgn, 276 !move.clip_rect.IsEmpty() && (flags & SWP_NOREDRAW) == 0); 277 278 #if defined(USE_AURA) 279 // When using the software compositor, if the clipping rectangle is empty 280 // then DeferWindowPos won't redraw the newly uncovered area under the 281 // plugin. 282 if (clip_rect_in_pixel.IsEmpty() && 283 !GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) { 284 RECT r; 285 GetClientRect(window, &r); 286 MapWindowPoints(window, parent, reinterpret_cast<POINT*>(&r), 2); 287 invalidate_rects.push_back(r); 288 } 289 #endif 290 } else { 291 flags |= SWP_NOMOVE; 292 flags |= SWP_NOSIZE; 293 } 294 295 gfx::Rect window_rect_in_pixel = 296 gfx::win::DIPToScreenRect(move.window_rect); 297 defer_window_pos_info = ::DeferWindowPos(defer_window_pos_info, 298 window, NULL, 299 window_rect_in_pixel.x(), 300 window_rect_in_pixel.y(), 301 window_rect_in_pixel.width(), 302 window_rect_in_pixel.height(), 303 flags); 304 305 if (!defer_window_pos_info) { 306 DCHECK(false) << "DeferWindowPos failed, so all plugin moves ignored."; 307 return; 308 } 309 } 310 311 ::EndDeferWindowPos(defer_window_pos_info); 312 313 #if defined(USE_AURA) 314 if (GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) { 315 for (size_t i = 0; i < moves.size(); ++i) { 316 const WebPluginGeometry& move = moves[i]; 317 RECT r; 318 GetWindowRect(move.window, &r); 319 gfx::Rect gr(r); 320 PaintEnumChildProc(move.window, reinterpret_cast<LPARAM>(&gr)); 321 } 322 } else { 323 for (size_t i = 0; i < invalidate_rects.size(); ++i) { 324 ::RedrawWindow( 325 parent, &invalidate_rects[i], NULL, 326 // These flags are from WebPluginDelegateImpl::NativeWndProc. 327 RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME | RDW_UPDATENOW); 328 } 329 } 330 #endif 331 } 332 333 // static 334 void RenderWidgetHostViewBase::PaintPluginWindowsHelper( 335 HWND parent, const gfx::Rect& damaged_screen_rect) { 336 LPARAM lparam = reinterpret_cast<LPARAM>(&damaged_screen_rect); 337 EnumChildWindows(parent, PaintEnumChildProc, lparam); 338 } 339 340 // static 341 void RenderWidgetHostViewBase::DetachPluginsHelper(HWND parent) { 342 // When a tab is closed all its child plugin windows are destroyed 343 // automatically. This happens before plugins get any notification that its 344 // instances are tearing down. 345 // 346 // Plugins like Quicktime assume that their windows will remain valid as long 347 // as they have plugin instances active. Quicktime crashes in this case 348 // because its windowing code cleans up an internal data structure that the 349 // handler for NPP_DestroyStream relies on. 350 // 351 // The fix is to detach plugin windows from web contents when it is going 352 // away. This will prevent the plugin windows from getting destroyed 353 // automatically. The detached plugin windows will get cleaned up in proper 354 // sequence as part of the usual cleanup when the plugin instance goes away. 355 EnumChildWindows(parent, DetachPluginWindowsCallbackInternal, NULL); 356 } 357 358 #endif // OS_WIN 359 360 namespace { 361 362 // How many microseconds apart input events should be flushed. 363 const int kFlushInputRateInUs = 16666; 364 365 } 366 367 RenderWidgetHostViewBase::RenderWidgetHostViewBase() 368 : popup_type_(blink::WebPopupTypeNone), 369 background_opaque_(true), 370 mouse_locked_(false), 371 showing_context_menu_(false), 372 selection_text_offset_(0), 373 selection_range_(gfx::Range::InvalidRange()), 374 current_device_scale_factor_(0), 375 current_display_rotation_(gfx::Display::ROTATE_0), 376 pinch_zoom_enabled_(content::IsPinchToZoomEnabled()), 377 renderer_frame_number_(0), 378 weak_factory_(this) { 379 } 380 381 RenderWidgetHostViewBase::~RenderWidgetHostViewBase() { 382 DCHECK(!mouse_locked_); 383 } 384 385 bool RenderWidgetHostViewBase::OnMessageReceived(const IPC::Message& msg){ 386 return false; 387 } 388 389 void RenderWidgetHostViewBase::SetBackgroundOpaque(bool opaque) { 390 background_opaque_ = opaque; 391 } 392 393 bool RenderWidgetHostViewBase::GetBackgroundOpaque() { 394 return background_opaque_; 395 } 396 397 gfx::Size RenderWidgetHostViewBase::GetPhysicalBackingSize() const { 398 gfx::NativeView view = GetNativeView(); 399 gfx::Display display = 400 gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view); 401 return gfx::ToCeiledSize(gfx::ScaleSize(GetRequestedRendererSize(), 402 display.device_scale_factor())); 403 } 404 405 float RenderWidgetHostViewBase::GetTopControlsLayoutHeight() const { 406 return 0.f; 407 } 408 409 void RenderWidgetHostViewBase::SelectionChanged(const base::string16& text, 410 size_t offset, 411 const gfx::Range& range) { 412 selection_text_ = text; 413 selection_text_offset_ = offset; 414 selection_range_.set_start(range.start()); 415 selection_range_.set_end(range.end()); 416 } 417 418 gfx::Size RenderWidgetHostViewBase::GetRequestedRendererSize() const { 419 return GetViewBounds().size(); 420 } 421 422 ui::TextInputClient* RenderWidgetHostViewBase::GetTextInputClient() { 423 NOTREACHED(); 424 return NULL; 425 } 426 427 bool RenderWidgetHostViewBase::IsShowingContextMenu() const { 428 return showing_context_menu_; 429 } 430 431 void RenderWidgetHostViewBase::SetShowingContextMenu(bool showing) { 432 DCHECK_NE(showing_context_menu_, showing); 433 showing_context_menu_ = showing; 434 } 435 436 base::string16 RenderWidgetHostViewBase::GetSelectedText() const { 437 if (!selection_range_.IsValid()) 438 return base::string16(); 439 return selection_text_.substr( 440 selection_range_.GetMin() - selection_text_offset_, 441 selection_range_.length()); 442 } 443 444 bool RenderWidgetHostViewBase::IsMouseLocked() { 445 return mouse_locked_; 446 } 447 448 InputEventAckState RenderWidgetHostViewBase::FilterInputEvent( 449 const blink::WebInputEvent& input_event) { 450 // By default, input events are simply forwarded to the renderer. 451 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; 452 } 453 454 void RenderWidgetHostViewBase::OnDidFlushInput() { 455 // The notification can safely be ignored by most implementations. 456 } 457 458 void RenderWidgetHostViewBase::OnSetNeedsFlushInput() { 459 if (flush_input_timer_.IsRunning()) 460 return; 461 462 flush_input_timer_.Start( 463 FROM_HERE, 464 base::TimeDelta::FromMicroseconds(kFlushInputRateInUs), 465 this, 466 &RenderWidgetHostViewBase::FlushInput); 467 } 468 469 void RenderWidgetHostViewBase::WheelEventAck( 470 const blink::WebMouseWheelEvent& event, 471 InputEventAckState ack_result) { 472 } 473 474 void RenderWidgetHostViewBase::GestureEventAck( 475 const blink::WebGestureEvent& event, 476 InputEventAckState ack_result) { 477 } 478 479 void RenderWidgetHostViewBase::SetPopupType(blink::WebPopupType popup_type) { 480 popup_type_ = popup_type; 481 } 482 483 blink::WebPopupType RenderWidgetHostViewBase::GetPopupType() { 484 return popup_type_; 485 } 486 487 BrowserAccessibilityManager* 488 RenderWidgetHostViewBase::CreateBrowserAccessibilityManager( 489 BrowserAccessibilityDelegate* delegate) { 490 NOTREACHED(); 491 return NULL; 492 } 493 494 void RenderWidgetHostViewBase::AccessibilityShowMenu(const gfx::Point& point) { 495 } 496 497 gfx::Point RenderWidgetHostViewBase::AccessibilityOriginInScreen( 498 const gfx::Rect& bounds) { 499 return bounds.origin(); 500 } 501 502 gfx::AcceleratedWidget 503 RenderWidgetHostViewBase::AccessibilityGetAcceleratedWidget() { 504 return gfx::kNullAcceleratedWidget; 505 } 506 507 gfx::NativeViewAccessible 508 RenderWidgetHostViewBase::AccessibilityGetNativeViewAccessible() { 509 return NULL; 510 } 511 512 void RenderWidgetHostViewBase::UpdateScreenInfo(gfx::NativeView view) { 513 RenderWidgetHostImpl* impl = NULL; 514 if (GetRenderWidgetHost()) 515 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost()); 516 517 if (impl) 518 impl->SendScreenRects(); 519 520 if (HasDisplayPropertyChanged(view) && impl) 521 impl->NotifyScreenInfoChanged(); 522 } 523 524 bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) { 525 gfx::Display display = 526 gfx::Screen::GetScreenFor(view)->GetDisplayNearestWindow(view); 527 if (current_display_area_ == display.work_area() && 528 current_device_scale_factor_ == display.device_scale_factor() && 529 current_display_rotation_ == display.rotation()) { 530 return false; 531 } 532 533 current_display_area_ = display.work_area(); 534 current_device_scale_factor_ = display.device_scale_factor(); 535 current_display_rotation_ = display.rotation(); 536 return true; 537 } 538 539 base::WeakPtr<RenderWidgetHostViewBase> RenderWidgetHostViewBase::GetWeakPtr() { 540 return weak_factory_.GetWeakPtr(); 541 } 542 543 scoped_ptr<SyntheticGestureTarget> 544 RenderWidgetHostViewBase::CreateSyntheticGestureTarget() { 545 RenderWidgetHostImpl* host = 546 RenderWidgetHostImpl::From(GetRenderWidgetHost()); 547 return scoped_ptr<SyntheticGestureTarget>( 548 new SyntheticGestureTargetBase(host)); 549 } 550 551 // Platform implementation should override this method to allow frame 552 // subscription. Frame subscriber is set to RenderProcessHost, which is 553 // platform independent. It should be set to the specific presenter on each 554 // platform. 555 bool RenderWidgetHostViewBase::CanSubscribeFrame() const { 556 NOTIMPLEMENTED(); 557 return false; 558 } 559 560 // Base implementation for this method sets the subscriber to RenderProcessHost, 561 // which is platform independent. Note: Implementation only support subscribing 562 // to accelerated composited frames. 563 void RenderWidgetHostViewBase::BeginFrameSubscription( 564 scoped_ptr<RenderWidgetHostViewFrameSubscriber> subscriber) { 565 RenderWidgetHostImpl* impl = NULL; 566 if (GetRenderWidgetHost()) 567 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost()); 568 if (!impl) 569 return; 570 RenderProcessHostImpl* render_process_host = 571 static_cast<RenderProcessHostImpl*>(impl->GetProcess()); 572 render_process_host->BeginFrameSubscription(impl->GetRoutingID(), 573 subscriber.Pass()); 574 } 575 576 void RenderWidgetHostViewBase::EndFrameSubscription() { 577 RenderWidgetHostImpl* impl = NULL; 578 if (GetRenderWidgetHost()) 579 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost()); 580 if (!impl) 581 return; 582 RenderProcessHostImpl* render_process_host = 583 static_cast<RenderProcessHostImpl*>(impl->GetProcess()); 584 render_process_host->EndFrameSubscription(impl->GetRoutingID()); 585 } 586 587 uint32 RenderWidgetHostViewBase::RendererFrameNumber() { 588 return renderer_frame_number_; 589 } 590 591 void RenderWidgetHostViewBase::DidReceiveRendererFrame() { 592 ++renderer_frame_number_; 593 } 594 595 void RenderWidgetHostViewBase::FlushInput() { 596 RenderWidgetHostImpl* impl = NULL; 597 if (GetRenderWidgetHost()) 598 impl = RenderWidgetHostImpl::From(GetRenderWidgetHost()); 599 if (!impl) 600 return; 601 impl->FlushInput(); 602 } 603 604 SkColorType RenderWidgetHostViewBase::PreferredReadbackFormat() { 605 return kN32_SkColorType; 606 } 607 608 gfx::Size RenderWidgetHostViewBase::GetVisibleViewportSize() const { 609 return GetViewBounds().size(); 610 } 611 612 void RenderWidgetHostViewBase::SetInsets(const gfx::Insets& insets) { 613 NOTIMPLEMENTED(); 614 } 615 616 // static 617 blink::WebScreenOrientationType 618 RenderWidgetHostViewBase::GetOrientationTypeForMobile( 619 const gfx::Display& display) { 620 int angle = display.RotationAsDegree(); 621 const gfx::Rect& bounds = display.bounds(); 622 623 // Whether the device's natural orientation is portrait. 624 bool natural_portrait = false; 625 if (angle == 0 || angle == 180) // The device is in its natural orientation. 626 natural_portrait = bounds.height() >= bounds.width(); 627 else 628 natural_portrait = bounds.height() <= bounds.width(); 629 630 switch (angle) { 631 case 0: 632 return natural_portrait ? blink::WebScreenOrientationPortraitPrimary 633 : blink::WebScreenOrientationLandscapePrimary; 634 case 90: 635 return natural_portrait ? blink::WebScreenOrientationLandscapePrimary 636 : blink::WebScreenOrientationPortraitSecondary; 637 case 180: 638 return natural_portrait ? blink::WebScreenOrientationPortraitSecondary 639 : blink::WebScreenOrientationLandscapeSecondary; 640 case 270: 641 return natural_portrait ? blink::WebScreenOrientationLandscapeSecondary 642 : blink::WebScreenOrientationPortraitPrimary; 643 default: 644 NOTREACHED(); 645 return blink::WebScreenOrientationPortraitPrimary; 646 } 647 } 648 649 // static 650 blink::WebScreenOrientationType 651 RenderWidgetHostViewBase::GetOrientationTypeForDesktop( 652 const gfx::Display& display) { 653 static int primary_landscape_angle = -1; 654 static int primary_portrait_angle = -1; 655 656 int angle = display.RotationAsDegree(); 657 const gfx::Rect& bounds = display.bounds(); 658 bool is_portrait = bounds.height() >= bounds.width(); 659 660 if (is_portrait && primary_portrait_angle == -1) 661 primary_portrait_angle = angle; 662 663 if (!is_portrait && primary_landscape_angle == -1) 664 primary_landscape_angle = angle; 665 666 if (is_portrait) { 667 return primary_portrait_angle == angle 668 ? blink::WebScreenOrientationPortraitPrimary 669 : blink::WebScreenOrientationPortraitSecondary; 670 } 671 672 return primary_landscape_angle == angle 673 ? blink::WebScreenOrientationLandscapePrimary 674 : blink::WebScreenOrientationLandscapeSecondary; 675 } 676 677 } // namespace content 678