Home | History | Annotate | Download | only in renderer_host
      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