| OLD | NEW |
| (Empty) | |
| 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/content/keyboard_ui_content.h" |
| 6 |
| 7 #include "base/command_line.h" |
| 8 #include "base/values.h" |
| 9 #include "content/public/browser/render_widget_host.h" |
| 10 #include "content/public/browser/render_widget_host_iterator.h" |
| 11 #include "content/public/browser/render_widget_host_view.h" |
| 12 #include "content/public/browser/site_instance.h" |
| 13 #include "content/public/browser/web_contents.h" |
| 14 #include "content/public/browser/web_contents.h" |
| 15 #include "content/public/browser/web_contents_delegate.h" |
| 16 #include "content/public/browser/web_contents_observer.h" |
| 17 #include "content/public/browser/web_ui.h" |
| 18 #include "content/public/common/bindings_policy.h" |
| 19 #include "ui/aura/layout_manager.h" |
| 20 #include "ui/aura/window.h" |
| 21 #include "ui/base/ime/input_method.h" |
| 22 #include "ui/base/ime/text_input_client.h" |
| 23 #include "ui/keyboard/content/keyboard_constants.h" |
| 24 #include "ui/keyboard/keyboard_controller.h" |
| 25 #include "ui/keyboard/keyboard_switches.h" |
| 26 #include "ui/keyboard/keyboard_util.h" |
| 27 #include "ui/wm/core/shadow.h" |
| 28 |
| 29 namespace { |
| 30 |
| 31 // The WebContentsDelegate for the keyboard. |
| 32 // The delegate deletes itself when the keyboard is destroyed. |
| 33 class KeyboardContentsDelegate : public content::WebContentsDelegate, |
| 34 public content::WebContentsObserver { |
| 35 public: |
| 36 explicit KeyboardContentsDelegate(keyboard::KeyboardUIContent* ui) |
| 37 : ui_(ui) {} |
| 38 ~KeyboardContentsDelegate() override {} |
| 39 |
| 40 private: |
| 41 // Overridden from content::WebContentsDelegate: |
| 42 content::WebContents* OpenURLFromTab( |
| 43 content::WebContents* source, |
| 44 const content::OpenURLParams& params) override { |
| 45 source->GetController().LoadURL( |
| 46 params.url, params.referrer, params.transition, params.extra_headers); |
| 47 Observe(source); |
| 48 return source; |
| 49 } |
| 50 |
| 51 bool CanDragEnter(content::WebContents* source, |
| 52 const content::DropData& data, |
| 53 blink::WebDragOperationsMask operations_allowed) override { |
| 54 return false; |
| 55 } |
| 56 |
| 57 bool ShouldCreateWebContents( |
| 58 content::WebContents* web_contents, |
| 59 int route_id, |
| 60 int main_frame_route_id, |
| 61 WindowContainerType window_container_type, |
| 62 const std::string& frame_name, |
| 63 const GURL& target_url, |
| 64 const std::string& partition_id, |
| 65 content::SessionStorageNamespace* session_storage_namespace) override { |
| 66 return false; |
| 67 } |
| 68 |
| 69 bool IsPopupOrPanel(const content::WebContents* source) const override { |
| 70 return true; |
| 71 } |
| 72 |
| 73 void MoveContents(content::WebContents* source, |
| 74 const gfx::Rect& pos) override { |
| 75 aura::Window* keyboard = ui_->GetKeyboardWindow(); |
| 76 // keyboard window must have been added to keyboard container window at this |
| 77 // point. Otherwise, wrong keyboard bounds is used and may cause problem as |
| 78 // described in crbug.com/367788. |
| 79 DCHECK(keyboard->parent()); |
| 80 // keyboard window bounds may not set to |pos| after this call. If keyboard |
| 81 // is in FULL_WIDTH mode, only the height of keyboard window will be |
| 82 // changed. |
| 83 keyboard->SetBounds(pos); |
| 84 } |
| 85 |
| 86 // Overridden from content::WebContentsDelegate: |
| 87 void RequestMediaAccessPermission( |
| 88 content::WebContents* web_contents, |
| 89 const content::MediaStreamRequest& request, |
| 90 const content::MediaResponseCallback& callback) override { |
| 91 ui_->RequestAudioInput(web_contents, request, callback); |
| 92 } |
| 93 |
| 94 // Overridden from content::WebContentsObserver: |
| 95 void WebContentsDestroyed() override { delete this; } |
| 96 |
| 97 keyboard::KeyboardUIContent* ui_; |
| 98 |
| 99 DISALLOW_COPY_AND_ASSIGN(KeyboardContentsDelegate); |
| 100 }; |
| 101 |
| 102 } // namespace |
| 103 |
| 104 namespace keyboard { |
| 105 |
| 106 class WindowBoundsChangeObserver : public aura::WindowObserver { |
| 107 public: |
| 108 explicit WindowBoundsChangeObserver(KeyboardUIContent* ui) : ui_(ui) {} |
| 109 ~WindowBoundsChangeObserver() override {} |
| 110 |
| 111 void AddObservedWindow(aura::Window* window) { |
| 112 if (!window->HasObserver(this)) { |
| 113 window->AddObserver(this); |
| 114 observed_windows_.insert(window); |
| 115 } |
| 116 } |
| 117 void RemoveAllObservedWindows() { |
| 118 for (std::set<aura::Window*>::iterator it = observed_windows_.begin(); |
| 119 it != observed_windows_.end(); ++it) |
| 120 (*it)->RemoveObserver(this); |
| 121 observed_windows_.clear(); |
| 122 } |
| 123 |
| 124 private: |
| 125 void OnWindowBoundsChanged(aura::Window* window, |
| 126 const gfx::Rect& old_bounds, |
| 127 const gfx::Rect& new_bounds) override { |
| 128 ui_->UpdateInsetsForWindow(window); |
| 129 } |
| 130 void OnWindowDestroyed(aura::Window* window) override { |
| 131 if (window->HasObserver(this)) |
| 132 window->RemoveObserver(this); |
| 133 observed_windows_.erase(window); |
| 134 } |
| 135 |
| 136 KeyboardUIContent* ui_; |
| 137 std::set<aura::Window*> observed_windows_; |
| 138 |
| 139 DISALLOW_COPY_AND_ASSIGN(WindowBoundsChangeObserver); |
| 140 }; |
| 141 |
| 142 KeyboardUIContent::KeyboardUIContent(content::BrowserContext* context) |
| 143 : browser_context_(context), |
| 144 default_url_(kKeyboardURL), |
| 145 window_bounds_observer_(new WindowBoundsChangeObserver(this)) { |
| 146 } |
| 147 |
| 148 KeyboardUIContent::~KeyboardUIContent() { |
| 149 ResetInsets(); |
| 150 } |
| 151 |
| 152 void KeyboardUIContent::LoadSystemKeyboard() { |
| 153 DCHECK(keyboard_contents_); |
| 154 if (keyboard_contents_->GetURL() != default_url_) { |
| 155 // TODO(bshe): The height of system virtual keyboard and IME virtual |
| 156 // keyboard may different. The height needs to be restored too. |
| 157 LoadContents(default_url_); |
| 158 } |
| 159 } |
| 160 |
| 161 void KeyboardUIContent::UpdateInsetsForWindow(aura::Window* window) { |
| 162 aura::Window* keyboard_window = GetKeyboardWindow(); |
| 163 scoped_ptr<content::RenderWidgetHostIterator> widgets( |
| 164 content::RenderWidgetHost::GetRenderWidgetHosts()); |
| 165 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { |
| 166 content::RenderWidgetHostView* view = widget->GetView(); |
| 167 if (view && window->Contains(view->GetNativeView())) { |
| 168 gfx::Rect window_bounds = view->GetNativeView()->GetBoundsInScreen(); |
| 169 gfx::Rect intersect = |
| 170 gfx::IntersectRects(window_bounds, keyboard_window->bounds()); |
| 171 int overlap = ShouldEnableInsets(window) ? intersect.height() : 0; |
| 172 if (overlap > 0 && overlap < window_bounds.height()) |
| 173 view->SetInsets(gfx::Insets(0, 0, overlap, 0)); |
| 174 else |
| 175 view->SetInsets(gfx::Insets()); |
| 176 return; |
| 177 } |
| 178 } |
| 179 } |
| 180 |
| 181 aura::Window* KeyboardUIContent::GetKeyboardWindow() { |
| 182 if (!keyboard_contents_) { |
| 183 content::BrowserContext* context = browser_context(); |
| 184 keyboard_contents_.reset(content::WebContents::Create( |
| 185 content::WebContents::CreateParams(context, |
| 186 content::SiteInstance::CreateForURL(context, |
| 187 GetVirtualKeyboardUrl())))); |
| 188 keyboard_contents_->SetDelegate(new KeyboardContentsDelegate(this)); |
| 189 SetupWebContents(keyboard_contents_.get()); |
| 190 LoadContents(GetVirtualKeyboardUrl()); |
| 191 keyboard_contents_->GetNativeView()->AddObserver(this); |
| 192 } |
| 193 |
| 194 return keyboard_contents_->GetNativeView(); |
| 195 } |
| 196 |
| 197 bool KeyboardUIContent::HasKeyboardWindow() const { |
| 198 return keyboard_contents_; |
| 199 } |
| 200 |
| 201 void KeyboardUIContent::ReloadKeyboardIfNeeded() { |
| 202 DCHECK(keyboard_contents_); |
| 203 if (keyboard_contents_->GetURL() != GetVirtualKeyboardUrl()) { |
| 204 if (keyboard_contents_->GetURL().GetOrigin() != |
| 205 GetVirtualKeyboardUrl().GetOrigin()) { |
| 206 // Sets keyboard window rectangle to 0 and close current page before |
| 207 // navigate to a keyboard in a different extension. This keeps the UX the |
| 208 // same as Android. Note we need to explicitly close current page as it |
| 209 // might try to resize keyboard window in javascript on a resize event. |
| 210 GetKeyboardWindow()->SetBounds(gfx::Rect()); |
| 211 keyboard_contents_->ClosePage(); |
| 212 keyboard_controller()->SetKeyboardMode(FULL_WIDTH); |
| 213 } |
| 214 LoadContents(GetVirtualKeyboardUrl()); |
| 215 } |
| 216 } |
| 217 |
| 218 void KeyboardUIContent::InitInsets(const gfx::Rect& new_bounds) { |
| 219 // Adjust the height of the viewport for visible windows on the primary |
| 220 // display. |
| 221 // TODO(kevers): Add EnvObserver to properly initialize insets if a |
| 222 // window is created while the keyboard is visible. |
| 223 scoped_ptr<content::RenderWidgetHostIterator> widgets( |
| 224 content::RenderWidgetHost::GetRenderWidgetHosts()); |
| 225 aura::Window* keyboard_window = GetKeyboardWindow(); |
| 226 aura::Window* root_window = keyboard_window->GetRootWindow(); |
| 227 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { |
| 228 content::RenderWidgetHostView* view = widget->GetView(); |
| 229 // Can be NULL, e.g. if the RenderWidget is being destroyed or |
| 230 // the render process crashed. |
| 231 if (view) { |
| 232 aura::Window* window = view->GetNativeView(); |
| 233 // If virtual keyboard failed to load, a widget that displays error |
| 234 // message will be created and adds as a child of the virtual keyboard |
| 235 // window. We want to avoid add BoundsChangedObserver to that window. |
| 236 if (!keyboard_window->Contains(window) && |
| 237 window->GetRootWindow() == root_window) { |
| 238 gfx::Rect window_bounds = window->GetBoundsInScreen(); |
| 239 gfx::Rect intersect = gfx::IntersectRects(window_bounds, |
| 240 new_bounds); |
| 241 int overlap = intersect.height(); |
| 242 if (overlap > 0 && overlap < window_bounds.height()) |
| 243 view->SetInsets(gfx::Insets(0, 0, overlap, 0)); |
| 244 else |
| 245 view->SetInsets(gfx::Insets()); |
| 246 AddBoundsChangedObserver(window); |
| 247 } |
| 248 } |
| 249 } |
| 250 } |
| 251 |
| 252 void KeyboardUIContent::ResetInsets() { |
| 253 const gfx::Insets insets; |
| 254 scoped_ptr<content::RenderWidgetHostIterator> widgets( |
| 255 content::RenderWidgetHost::GetRenderWidgetHosts()); |
| 256 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { |
| 257 content::RenderWidgetHostView* view = widget->GetView(); |
| 258 if (view) |
| 259 view->SetInsets(insets); |
| 260 } |
| 261 window_bounds_observer_->RemoveAllObservedWindows(); |
| 262 } |
| 263 |
| 264 void KeyboardUIContent::SetupWebContents(content::WebContents* contents) { |
| 265 } |
| 266 |
| 267 void KeyboardUIContent::OnWindowBoundsChanged(aura::Window* window, |
| 268 const gfx::Rect& old_bounds, |
| 269 const gfx::Rect& new_bounds) { |
| 270 if (!shadow_) { |
| 271 shadow_.reset(new wm::Shadow()); |
| 272 shadow_->Init(wm::Shadow::STYLE_ACTIVE); |
| 273 shadow_->layer()->SetVisible(true); |
| 274 DCHECK(keyboard_contents_->GetNativeView()->parent()); |
| 275 keyboard_contents_->GetNativeView()->parent()->layer()->Add( |
| 276 shadow_->layer()); |
| 277 } |
| 278 |
| 279 shadow_->SetContentBounds(new_bounds); |
| 280 } |
| 281 |
| 282 void KeyboardUIContent::OnWindowDestroyed(aura::Window* window) { |
| 283 window->RemoveObserver(this); |
| 284 } |
| 285 |
| 286 void KeyboardUIContent::LoadContents(const GURL& url) { |
| 287 if (keyboard_contents_) { |
| 288 content::OpenURLParams params( |
| 289 url, |
| 290 content::Referrer(), |
| 291 SINGLETON_TAB, |
| 292 ui::PAGE_TRANSITION_AUTO_TOPLEVEL, |
| 293 false); |
| 294 keyboard_contents_->OpenURL(params); |
| 295 } |
| 296 } |
| 297 |
| 298 const GURL& KeyboardUIContent::GetVirtualKeyboardUrl() { |
| 299 if (keyboard::IsInputViewEnabled()) { |
| 300 const GURL& override_url = GetOverrideContentUrl(); |
| 301 return override_url.is_valid() ? override_url : default_url_; |
| 302 } else { |
| 303 return default_url_; |
| 304 } |
| 305 } |
| 306 |
| 307 bool KeyboardUIContent::ShouldEnableInsets(aura::Window* window) { |
| 308 aura::Window* keyboard_window = GetKeyboardWindow(); |
| 309 return (keyboard_window->GetRootWindow() == window->GetRootWindow() && |
| 310 keyboard::IsKeyboardOverscrollEnabled() && |
| 311 keyboard_window->IsVisible() && |
| 312 keyboard_controller()->keyboard_visible()); |
| 313 } |
| 314 |
| 315 void KeyboardUIContent::AddBoundsChangedObserver(aura::Window* window) { |
| 316 aura::Window* target_window = window ? window->GetToplevelWindow() : nullptr; |
| 317 if (target_window) |
| 318 window_bounds_observer_->AddObservedWindow(target_window); |
| 319 } |
| 320 |
| 321 } // namespace keyboard |
| OLD | NEW |