1// Copyright (c) 2013 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 "ui/keyboard/keyboard_controller_proxy.h"
6
7#include "base/command_line.h"
8#include "base/values.h"
9#include "content/public/browser/site_instance.h"
10#include "content/public/browser/web_contents.h"
11#include "content/public/browser/web_contents.h"
12#include "content/public/browser/web_contents_delegate.h"
13#include "content/public/browser/web_contents_observer.h"
14#include "content/public/browser/web_ui.h"
15#include "content/public/common/bindings_policy.h"
16#include "ui/aura/layout_manager.h"
17#include "ui/aura/window.h"
18#include "ui/base/ime/input_method.h"
19#include "ui/base/ime/text_input_client.h"
20#include "ui/keyboard/keyboard_constants.h"
21#include "ui/keyboard/keyboard_switches.h"
22#include "ui/keyboard/keyboard_util.h"
23#include "ui/wm/core/shadow.h"
24
25namespace {
26
27// The WebContentsDelegate for the keyboard.
28// The delegate deletes itself when the keyboard is destroyed.
29class KeyboardContentsDelegate : public content::WebContentsDelegate,
30                                 public content::WebContentsObserver {
31 public:
32  KeyboardContentsDelegate(keyboard::KeyboardControllerProxy* proxy)
33      : proxy_(proxy) {}
34  virtual ~KeyboardContentsDelegate() {}
35
36 private:
37  // Overridden from content::WebContentsDelegate:
38  virtual content::WebContents* OpenURLFromTab(
39      content::WebContents* source,
40      const content::OpenURLParams& params) OVERRIDE {
41    source->GetController().LoadURL(
42        params.url, params.referrer, params.transition, params.extra_headers);
43    Observe(source);
44    return source;
45  }
46
47  virtual bool IsPopupOrPanel(
48      const content::WebContents* source) const OVERRIDE {
49    return true;
50  }
51
52  virtual void MoveContents(content::WebContents* source,
53                            const gfx::Rect& pos) OVERRIDE {
54    aura::Window* keyboard = proxy_->GetKeyboardWindow();
55    // keyboard window must have been added to keyboard container window at this
56    // point. Otherwise, wrong keyboard bounds is used and may cause problem as
57    // described in crbug.com/367788.
58    DCHECK(keyboard->parent());
59    gfx::Rect bounds = keyboard->bounds();
60    int new_height = pos.height();
61    bounds.set_y(bounds.y() + bounds.height() - new_height);
62    bounds.set_height(new_height);
63    keyboard->SetBounds(bounds);
64  }
65
66  // Overridden from content::WebContentsDelegate:
67  virtual void RequestMediaAccessPermission(content::WebContents* web_contents,
68      const content::MediaStreamRequest& request,
69      const content::MediaResponseCallback& callback) OVERRIDE {
70    proxy_->RequestAudioInput(web_contents, request, callback);
71  }
72
73  // Overridden from content::WebContentsObserver:
74  virtual void WebContentsDestroyed() OVERRIDE {
75    delete this;
76  }
77
78  keyboard::KeyboardControllerProxy* proxy_;
79
80  DISALLOW_COPY_AND_ASSIGN(KeyboardContentsDelegate);
81};
82
83}  // namespace
84
85namespace keyboard {
86
87KeyboardControllerProxy::KeyboardControllerProxy()
88    : default_url_(kKeyboardURL) {
89}
90
91KeyboardControllerProxy::~KeyboardControllerProxy() {
92}
93
94const GURL& KeyboardControllerProxy::GetVirtualKeyboardUrl() {
95  if (keyboard::IsInputViewEnabled()) {
96    const GURL& override_url = GetOverrideContentUrl();
97    return override_url.is_valid() ? override_url : default_url_;
98  } else {
99    return default_url_;
100  }
101}
102
103void KeyboardControllerProxy::LoadContents(const GURL& url) {
104  if (keyboard_contents_) {
105    content::OpenURLParams params(
106        url,
107        content::Referrer(),
108        SINGLETON_TAB,
109        ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
110        false);
111    keyboard_contents_->OpenURL(params);
112  }
113}
114
115aura::Window* KeyboardControllerProxy::GetKeyboardWindow() {
116  if (!keyboard_contents_) {
117    content::BrowserContext* context = GetBrowserContext();
118    keyboard_contents_.reset(content::WebContents::Create(
119        content::WebContents::CreateParams(context,
120            content::SiteInstance::CreateForURL(context,
121                                                GetVirtualKeyboardUrl()))));
122    keyboard_contents_->SetDelegate(new KeyboardContentsDelegate(this));
123    SetupWebContents(keyboard_contents_.get());
124    LoadContents(GetVirtualKeyboardUrl());
125    keyboard_contents_->GetNativeView()->AddObserver(this);
126  }
127
128  return keyboard_contents_->GetNativeView();
129}
130
131bool KeyboardControllerProxy::HasKeyboardWindow() const {
132  return keyboard_contents_;
133}
134
135void KeyboardControllerProxy::ShowKeyboardContainer(aura::Window* container) {
136  GetKeyboardWindow()->Show();
137  container->Show();
138}
139
140void KeyboardControllerProxy::HideKeyboardContainer(aura::Window* container) {
141  container->Hide();
142  GetKeyboardWindow()->Hide();
143}
144
145void KeyboardControllerProxy::SetUpdateInputType(ui::TextInputType type) {
146}
147
148void KeyboardControllerProxy::EnsureCaretInWorkArea() {
149  if (GetInputMethod()->GetTextInputClient()) {
150    aura::Window* keyboard_window = GetKeyboardWindow();
151    aura::Window* root_window = keyboard_window->GetRootWindow();
152    gfx::Rect available_bounds = root_window->bounds();
153    gfx::Rect keyboard_bounds = keyboard_window->bounds();
154    available_bounds.set_height(available_bounds.height() -
155        keyboard_bounds.height());
156    GetInputMethod()->GetTextInputClient()->EnsureCaretInRect(available_bounds);
157  }
158}
159
160void KeyboardControllerProxy::LoadSystemKeyboard() {
161  DCHECK(keyboard_contents_);
162  if (keyboard_contents_->GetURL() != default_url_) {
163    // TODO(bshe): The height of system virtual keyboard and IME virtual
164    // keyboard may different. The height needs to be restored too.
165    LoadContents(default_url_);
166  }
167}
168
169void KeyboardControllerProxy::ReloadKeyboardIfNeeded() {
170  DCHECK(keyboard_contents_);
171  if (keyboard_contents_->GetURL() != GetVirtualKeyboardUrl()) {
172    if (keyboard_contents_->GetURL().GetOrigin() !=
173        GetVirtualKeyboardUrl().GetOrigin()) {
174      // Sets keyboard window height to 0 before navigate to a keyboard in a
175      // different extension. This keeps the UX the same as Android.
176      gfx::Rect bounds = GetKeyboardWindow()->bounds();
177      bounds.set_y(bounds.y() + bounds.height());
178      bounds.set_height(0);
179      GetKeyboardWindow()->SetBounds(bounds);
180    }
181    LoadContents(GetVirtualKeyboardUrl());
182  }
183}
184
185void KeyboardControllerProxy::SetupWebContents(content::WebContents* contents) {
186}
187
188void KeyboardControllerProxy::OnWindowBoundsChanged(
189    aura::Window* window,
190    const gfx::Rect& old_bounds,
191    const gfx::Rect& new_bounds) {
192  if (!shadow_) {
193    shadow_.reset(new wm::Shadow());
194    shadow_->Init(wm::Shadow::STYLE_ACTIVE);
195    shadow_->layer()->SetVisible(true);
196    DCHECK(keyboard_contents_->GetNativeView()->parent());
197    keyboard_contents_->GetNativeView()->parent()->layer()->Add(
198        shadow_->layer());
199  }
200
201  shadow_->SetContentBounds(new_bounds);
202}
203
204void KeyboardControllerProxy::OnWindowDestroyed(aura::Window* window) {
205  window->RemoveObserver(this);
206}
207
208}  // namespace keyboard
209