Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / ui / views / controls / webview / webview.cc
index 0a7734e..7e31097 100644 (file)
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_view.h"
 #include "ipc/ipc_message.h"
-#include "ui/base/accessibility/accessibility_types.h"
-#include "ui/base/accessibility/accessible_view_state.h"
+#include "ui/accessibility/ax_enums.h"
+#include "ui/accessibility/ax_view_state.h"
+#include "ui/aura/window.h"
+#include "ui/base/ui_base_switches_util.h"
 #include "ui/events/event.h"
 #include "ui/views/accessibility/native_view_accessibility.h"
 #include "ui/views/controls/native/native_view_host.h"
@@ -29,16 +30,17 @@ const char WebView::kViewClassName[] = "WebView";
 // WebView, public:
 
 WebView::WebView(content::BrowserContext* browser_context)
-    : wcv_holder_(new NativeViewHost),
+    : holder_(new NativeViewHost()),
       embed_fullscreen_widget_mode_enabled_(false),
       is_embedding_fullscreen_widget_(false),
       browser_context_(browser_context),
       allow_accelerators_(false) {
-  AddChildView(wcv_holder_);
+  AddChildView(holder_);  // Takes ownership of |holder_|.
   NativeViewAccessibility::RegisterWebView(this);
 }
 
 WebView::~WebView() {
+  SetWebContents(NULL);  // Make sure all necessary tear-down takes place.
   NativeViewAccessibility::UnregisterWebView(this);
 }
 
@@ -55,41 +57,35 @@ void WebView::SetWebContents(content::WebContents* replacement) {
   if (replacement == web_contents())
     return;
   DetachWebContents();
-  if (wc_owner_ != replacement)
-    wc_owner_.reset();
   WebContentsObserver::Observe(replacement);
   // web_contents() now returns |replacement| from here onwards.
+  SetFocusable(!!web_contents());
+  if (wc_owner_ != replacement)
+    wc_owner_.reset();
   if (embed_fullscreen_widget_mode_enabled_) {
     is_embedding_fullscreen_widget_ =
         web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
   } else {
-    is_embedding_fullscreen_widget_ = false;
+    DCHECK(!is_embedding_fullscreen_widget_);
   }
   AttachWebContents();
+  NotifyMaybeTextInputClientChanged();
 }
 
 void WebView::SetEmbedFullscreenWidgetMode(bool enable) {
-  bool should_be_embedded = enable;
-  if (!embed_fullscreen_widget_mode_enabled_ && enable) {
-    DCHECK(!is_embedding_fullscreen_widget_);
-    embed_fullscreen_widget_mode_enabled_ = true;
-    should_be_embedded =
-        web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
-  } else if (embed_fullscreen_widget_mode_enabled_ && !enable) {
-    embed_fullscreen_widget_mode_enabled_ = false;
-  }
-  if (should_be_embedded != is_embedding_fullscreen_widget_)
-    ReattachForFullscreenChange(should_be_embedded);
+  DCHECK(!web_contents())
+      << "Cannot change mode while a WebContents is attached.";
+  embed_fullscreen_widget_mode_enabled_ = enable;
 }
 
 void WebView::LoadInitialURL(const GURL& url) {
   GetWebContents()->GetController().LoadURL(
-      url, content::Referrer(), content::PAGE_TRANSITION_AUTO_TOPLEVEL,
+      url, content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
       std::string());
 }
 
 void WebView::SetFastResize(bool fast_resize) {
-  wcv_holder_->set_fast_resize(fast_resize);
+  holder_->set_fast_resize(fast_resize);
 }
 
 void WebView::OnWebContentsFocused(content::WebContents* web_contents) {
@@ -110,8 +106,80 @@ const char* WebView::GetClassName() const {
   return kViewClassName;
 }
 
+ui::TextInputClient* WebView::GetTextInputClient() {
+  // This function delegates the text input handling to the underlying
+  // content::RenderWidgetHostView.  So when the underlying RWHV is destroyed or
+  // replaced with another one, we have to notify the FocusManager through
+  // FocusManager::OnTextInputClientChanged() that the focused TextInputClient
+  // needs to be updated.
+  if (switches::IsTextInputFocusManagerEnabled() &&
+      web_contents() && !web_contents()->IsBeingDestroyed()) {
+    content::RenderWidgetHostView* host_view =
+        is_embedding_fullscreen_widget_ ?
+        web_contents()->GetFullscreenRenderWidgetHostView() :
+        web_contents()->GetRenderWidgetHostView();
+    if (host_view)
+      return host_view->GetTextInputClient();
+  }
+  return NULL;
+}
+
+scoped_ptr<content::WebContents> WebView::SwapWebContents(
+    scoped_ptr<content::WebContents> new_web_contents) {
+  if (wc_owner_)
+    wc_owner_->SetDelegate(NULL);
+  scoped_ptr<content::WebContents> old_web_contents(wc_owner_.Pass());
+  wc_owner_ = new_web_contents.Pass();
+  if (wc_owner_)
+    wc_owner_->SetDelegate(this);
+  SetWebContents(wc_owner_.get());
+  return old_web_contents.Pass();
+}
+
 void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
-  wcv_holder_->SetSize(bounds().size());
+  // In most cases, the holder is simply sized to fill this WebView's bounds.
+  // Only WebContentses that are in fullscreen mode and being screen-captured
+  // will engage the special layout/sizing behavior.
+  gfx::Rect holder_bounds(bounds().size());
+  if (!embed_fullscreen_widget_mode_enabled_ ||
+      !web_contents() ||
+      web_contents()->GetCapturerCount() == 0 ||
+      web_contents()->GetPreferredSize().IsEmpty() ||
+      !(is_embedding_fullscreen_widget_ ||
+        (web_contents()->GetDelegate() &&
+         web_contents()->GetDelegate()->
+             IsFullscreenForTabOrPending(web_contents())))) {
+    holder_->SetBoundsRect(holder_bounds);
+    return;
+  }
+
+  // Size the holder to the capture video resolution and center it.  If this
+  // WebView is not large enough to contain the holder at the preferred size,
+  // scale down to fit (preserving aspect ratio).
+  const gfx::Size capture_size = web_contents()->GetPreferredSize();
+  if (capture_size.width() <= holder_bounds.width() &&
+      capture_size.height() <= holder_bounds.height()) {
+    // No scaling, just centering.
+    holder_bounds.ClampToCenteredSize(capture_size);
+  } else {
+    // Scale down, preserving aspect ratio, and center.
+    // TODO(miu): This is basically media::ComputeLetterboxRegion(), and it
+    // looks like others have written this code elsewhere.  Let's considate
+    // into a shared function ui/gfx/geometry or around there.
+    const int64 x = static_cast<int64>(capture_size.width()) *
+        holder_bounds.height();
+    const int64 y = static_cast<int64>(capture_size.height()) *
+        holder_bounds.width();
+    if (y < x) {
+      holder_bounds.ClampToCenteredSize(gfx::Size(
+          holder_bounds.width(), static_cast<int>(y / capture_size.width())));
+    } else {
+      holder_bounds.ClampToCenteredSize(gfx::Size(
+          static_cast<int>(x / capture_size.height()), holder_bounds.height()));
+    }
+  }
+
+  holder_->SetBoundsRect(holder_bounds);
 }
 
 void WebView::ViewHierarchyChanged(
@@ -132,23 +200,9 @@ bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
   return web_contents() && !web_contents()->IsCrashed();
 }
 
-bool WebView::IsFocusable() const {
-  // We need to be focusable when our contents is not a view hierarchy, as
-  // clicking on the contents needs to focus us.
-  return !!web_contents();
-}
-
 void WebView::OnFocus() {
-  if (!web_contents())
-    return;
-  if (is_embedding_fullscreen_widget_) {
-    content::RenderWidgetHostView* const current_fs_view =
-        web_contents()->GetFullscreenRenderWidgetHostView();
-    if (current_fs_view)
-      current_fs_view->Focus();
-  } else {
-    web_contents()->GetView()->Focus();
-  }
+  if (web_contents())
+    web_contents()->Focus();
 }
 
 void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
@@ -156,8 +210,8 @@ void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
     web_contents()->FocusThroughTabTraversal(reverse);
 }
 
-void WebView::GetAccessibleState(ui::AccessibleViewState* state) {
-  state->role = ui::AccessibilityTypes::ROLE_GROUPING;
+void WebView::GetAccessibleState(ui::AXViewState* state) {
+  state->role = ui::AX_ROLE_GROUP;
 }
 
 gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
@@ -170,7 +224,7 @@ gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
   return View::GetNativeViewAccessible();
 }
 
-gfx::Size WebView::GetPreferredSize() {
+gfx::Size WebView::GetPreferredSize() const {
   if (preferred_size_ == gfx::Size())
     return View::GetPreferredSize();
   else
@@ -194,19 +248,20 @@ bool WebView::EmbedsFullscreenWidget() const {
 ////////////////////////////////////////////////////////////////////////////////
 // WebView, content::WebContentsObserver implementation:
 
+void WebView::RenderViewDeleted(content::RenderViewHost* render_view_host) {
+  NotifyMaybeTextInputClientChanged();
+}
+
+void WebView::RenderProcessGone(base::TerminationStatus status) {
+  NotifyMaybeTextInputClientChanged();
+}
+
 void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
                                     content::RenderViewHost* new_host) {
   FocusManager* const focus_manager = GetFocusManager();
   if (focus_manager && focus_manager->GetFocusedView() == this)
     OnFocus();
-}
-
-void WebView::WebContentsDestroyed(content::WebContents* web_contents) {
-  // We watch for destruction of WebContents that we host but do not own. If we
-  // own a WebContents that is being destroyed, we're doing the destroying, so
-  // we don't want to recursively tear it down while it's being torn down.
-  if (!wc_owner_.get())
-    SetWebContents(NULL);
+  NotifyMaybeTextInputClientChanged();
 }
 
 void WebView::DidShowFullscreenWidget(int routing_id) {
@@ -219,6 +274,19 @@ void WebView::DidDestroyFullscreenWidget(int routing_id) {
     ReattachForFullscreenChange(false);
 }
 
+void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen) {
+  if (embed_fullscreen_widget_mode_enabled_)
+    ReattachForFullscreenChange(entered_fullscreen);
+}
+
+void WebView::DidAttachInterstitialPage() {
+  NotifyMaybeTextInputClientChanged();
+}
+
+void WebView::DidDetachInterstitialPage() {
+  NotifyMaybeTextInputClientChanged();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // WebView, private:
 
@@ -230,10 +298,18 @@ void WebView::AttachWebContents() {
 
   const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ?
       web_contents()->GetFullscreenRenderWidgetHostView()->GetNativeView() :
-      web_contents()->GetView()->GetNativeView();
-  if (wcv_holder_->native_view() == view_to_attach)
+      web_contents()->GetNativeView();
+  OnBoundsChanged(bounds());
+  if (holder_->native_view() == view_to_attach)
     return;
-  wcv_holder_->Attach(view_to_attach);
+
+  // The WCV needs to be parented before making it visible.
+  holder_->Attach(view_to_attach);
+
+  // Fullscreen widgets are not parented by a WebContentsView. Their visibility
+  // is controlled by content i.e. (RenderWidgetHost)
+  if (!is_embedding_fullscreen_widget_)
+    view_to_attach->Show();
 
   // The view will not be focused automatically when it is attached, so we need
   // to pass on focus to it if the FocusManager thinks the view is focused. Note
@@ -242,7 +318,7 @@ void WebView::AttachWebContents() {
   if (focus_manager && focus_manager->GetFocusedView() == this)
     OnFocus();
 
-#if defined(OS_WIN) && defined(USE_AURA)
+#if defined(OS_WIN)
   if (!is_embedding_fullscreen_widget_) {
     web_contents()->SetParentNativeViewAccessible(
         parent()->GetNativeViewAccessible());
@@ -252,31 +328,43 @@ void WebView::AttachWebContents() {
 
 void WebView::DetachWebContents() {
   if (web_contents()) {
-    wcv_holder_->Detach();
+    // Fullscreen widgets are not parented by a WebContentsView. Their
+    // visibility is controlled by content i.e. (RenderWidgetHost).
+    if (!is_embedding_fullscreen_widget_)
+      web_contents()->GetNativeView()->Hide();
+
+    holder_->Detach();
 #if defined(OS_WIN)
-    if (!is_embedding_fullscreen_widget_) {
-#if !defined(USE_AURA)
-      // TODO(beng): This should either not be necessary, or be done implicitly
-      // by NativeViewHostWin on Detach(). As it stands, this is needed so that
-      // the of the detached contents knows to tell the renderer it's been
-      // hidden.
-      //
-      // Moving this out of here would also mean we wouldn't be potentially
-      // calling member functions on a half-destroyed WebContents.
-      ShowWindow(web_contents()->GetView()->GetNativeView(), SW_HIDE);
-#else
+    if (!is_embedding_fullscreen_widget_)
       web_contents()->SetParentNativeViewAccessible(NULL);
 #endif
-    }
-#endif
   }
 }
 
 void WebView::ReattachForFullscreenChange(bool enter_fullscreen) {
-  DetachWebContents();
-  is_embedding_fullscreen_widget_ = enter_fullscreen &&
+  DCHECK(embed_fullscreen_widget_mode_enabled_);
+  const bool web_contents_has_separate_fs_widget =
       web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
-  AttachWebContents();
+  if (is_embedding_fullscreen_widget_ || web_contents_has_separate_fs_widget) {
+    // Shutting down or starting up the embedding of the separate fullscreen
+    // widget.  Need to detach and re-attach to a different native view.
+    DetachWebContents();
+    is_embedding_fullscreen_widget_ =
+        enter_fullscreen && web_contents_has_separate_fs_widget;
+    AttachWebContents();
+  } else {
+    // Entering or exiting "non-Flash" fullscreen mode, where the native view is
+    // the same.  So, do not change attachment.
+    OnBoundsChanged(bounds());
+  }
+  NotifyMaybeTextInputClientChanged();
+}
+
+void WebView::NotifyMaybeTextInputClientChanged() {
+  // Update the TextInputClient as needed; see GetTextInputClient().
+  FocusManager* const focus_manager = GetFocusManager();
+  if (focus_manager)
+    focus_manager->OnTextInputClientChanged(this);
 }
 
 content::WebContents* WebView::CreateWebContents(