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