Add ICU message format support
[chromium-blink-merge.git] / ui / views / controls / webview / webview.cc
blobc9e3105cd60f6e066da5703303b044c5562b0733
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.
5 #include "ui/views/controls/webview/webview.h"
7 #include "content/public/browser/browser_accessibility_state.h"
8 #include "content/public/browser/browser_context.h"
9 #include "content/public/browser/navigation_controller.h"
10 #include "content/public/browser/render_process_host.h"
11 #include "content/public/browser/render_view_host.h"
12 #include "content/public/browser/render_widget_host_view.h"
13 #include "content/public/browser/web_contents.h"
14 #include "ipc/ipc_message.h"
15 #include "ui/accessibility/ax_enums.h"
16 #include "ui/accessibility/ax_view_state.h"
17 #include "ui/events/event.h"
18 #include "ui/views/controls/native/native_view_host.h"
19 #include "ui/views/focus/focus_manager.h"
20 #include "ui/views/views_delegate.h"
22 namespace views {
24 // static
25 const char WebView::kViewClassName[] = "WebView";
27 ////////////////////////////////////////////////////////////////////////////////
28 // WebView, public:
30 WebView::WebView(content::BrowserContext* browser_context)
31 : holder_(new NativeViewHost()),
32 observing_render_process_host_(nullptr),
33 embed_fullscreen_widget_mode_enabled_(false),
34 is_embedding_fullscreen_widget_(false),
35 browser_context_(browser_context),
36 allow_accelerators_(false) {
37 AddChildView(holder_); // Takes ownership of |holder_|.
40 WebView::~WebView() {
41 SetWebContents(NULL); // Make sure all necessary tear-down takes place.
44 content::WebContents* WebView::GetWebContents() {
45 if (!web_contents()) {
46 wc_owner_.reset(CreateWebContents(browser_context_));
47 wc_owner_->SetDelegate(this);
48 SetWebContents(wc_owner_.get());
50 return web_contents();
53 void WebView::SetWebContents(content::WebContents* replacement) {
54 if (replacement == web_contents())
55 return;
56 DetachWebContents();
57 WebContentsObserver::Observe(replacement);
58 if (observing_render_process_host_) {
59 observing_render_process_host_->RemoveObserver(this);
60 observing_render_process_host_ = nullptr;
62 if (web_contents() && web_contents()->GetRenderProcessHost()) {
63 observing_render_process_host_ = web_contents()->GetRenderProcessHost();
64 observing_render_process_host_->AddObserver(this);
66 // web_contents() now returns |replacement| from here onwards.
67 SetFocusable(!!web_contents());
68 if (wc_owner_ != replacement)
69 wc_owner_.reset();
70 if (embed_fullscreen_widget_mode_enabled_) {
71 is_embedding_fullscreen_widget_ =
72 web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
73 } else {
74 DCHECK(!is_embedding_fullscreen_widget_);
76 AttachWebContents();
77 NotifyMaybeTextInputClientAndAccessibilityChanged();
80 void WebView::SetEmbedFullscreenWidgetMode(bool enable) {
81 DCHECK(!web_contents())
82 << "Cannot change mode while a WebContents is attached.";
83 embed_fullscreen_widget_mode_enabled_ = enable;
86 void WebView::LoadInitialURL(const GURL& url) {
87 GetWebContents()->GetController().LoadURL(
88 url, content::Referrer(), ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
89 std::string());
92 void WebView::SetFastResize(bool fast_resize) {
93 holder_->set_fast_resize(fast_resize);
96 void WebView::SetResizeBackgroundColor(SkColor resize_background_color) {
97 holder_->set_resize_background_color(resize_background_color);
100 void WebView::SetPreferredSize(const gfx::Size& preferred_size) {
101 preferred_size_ = preferred_size;
102 PreferredSizeChanged();
105 ////////////////////////////////////////////////////////////////////////////////
106 // WebView, View overrides:
108 const char* WebView::GetClassName() const {
109 return kViewClassName;
112 scoped_ptr<content::WebContents> WebView::SwapWebContents(
113 scoped_ptr<content::WebContents> new_web_contents) {
114 if (wc_owner_)
115 wc_owner_->SetDelegate(NULL);
116 scoped_ptr<content::WebContents> old_web_contents(wc_owner_.Pass());
117 wc_owner_ = new_web_contents.Pass();
118 if (wc_owner_)
119 wc_owner_->SetDelegate(this);
120 SetWebContents(wc_owner_.get());
121 return old_web_contents.Pass();
124 void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
125 // In most cases, the holder is simply sized to fill this WebView's bounds.
126 // Only WebContentses that are in fullscreen mode and being screen-captured
127 // will engage the special layout/sizing behavior.
128 gfx::Rect holder_bounds(bounds().size());
129 if (!embed_fullscreen_widget_mode_enabled_ ||
130 !web_contents() ||
131 web_contents()->GetCapturerCount() == 0 ||
132 web_contents()->GetPreferredSize().IsEmpty() ||
133 !(is_embedding_fullscreen_widget_ ||
134 (web_contents()->GetDelegate() &&
135 web_contents()->GetDelegate()->
136 IsFullscreenForTabOrPending(web_contents())))) {
137 holder_->SetBoundsRect(holder_bounds);
138 return;
141 // Size the holder to the capture video resolution and center it. If this
142 // WebView is not large enough to contain the holder at the preferred size,
143 // scale down to fit (preserving aspect ratio).
144 const gfx::Size capture_size = web_contents()->GetPreferredSize();
145 if (capture_size.width() <= holder_bounds.width() &&
146 capture_size.height() <= holder_bounds.height()) {
147 // No scaling, just centering.
148 holder_bounds.ClampToCenteredSize(capture_size);
149 } else {
150 // Scale down, preserving aspect ratio, and center.
151 // TODO(miu): This is basically media::ComputeLetterboxRegion(), and it
152 // looks like others have written this code elsewhere. Let's considate
153 // into a shared function ui/gfx/geometry or around there.
154 const int64 x = static_cast<int64>(capture_size.width()) *
155 holder_bounds.height();
156 const int64 y = static_cast<int64>(capture_size.height()) *
157 holder_bounds.width();
158 if (y < x) {
159 holder_bounds.ClampToCenteredSize(gfx::Size(
160 holder_bounds.width(), static_cast<int>(y / capture_size.width())));
161 } else {
162 holder_bounds.ClampToCenteredSize(gfx::Size(
163 static_cast<int>(x / capture_size.height()), holder_bounds.height()));
167 holder_->SetBoundsRect(holder_bounds);
170 void WebView::ViewHierarchyChanged(
171 const ViewHierarchyChangedDetails& details) {
172 if (details.is_add)
173 AttachWebContents();
176 bool WebView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
177 if (allow_accelerators_)
178 return FocusManager::IsTabTraversalKeyEvent(event);
180 // Don't look-up accelerators or tab-traversal if we are showing a non-crashed
181 // TabContents.
182 // We'll first give the page a chance to process the key events. If it does
183 // not process them, they'll be returned to us and we'll treat them as
184 // accelerators then.
185 return web_contents() && !web_contents()->IsCrashed();
188 bool WebView::OnMousePressed(const ui::MouseEvent& event) {
189 // A left-click within WebView is a request to focus. The area within the
190 // native view child is excluded since it will be handling mouse pressed
191 // events itself (http://crbug.com/436192).
192 if (event.IsOnlyLeftMouseButton() && HitTestPoint(event.location())) {
193 gfx::Point location_in_holder = event.location();
194 ConvertPointToTarget(this, holder_, &location_in_holder);
195 if (!holder_->HitTestPoint(location_in_holder)) {
196 RequestFocus();
197 return true;
200 return View::OnMousePressed(event);
203 void WebView::OnFocus() {
204 if (web_contents())
205 web_contents()->Focus();
208 void WebView::AboutToRequestFocusFromTabTraversal(bool reverse) {
209 if (web_contents())
210 web_contents()->FocusThroughTabTraversal(reverse);
213 void WebView::GetAccessibleState(ui::AXViewState* state) {
214 state->role = ui::AX_ROLE_WEB_VIEW;
217 gfx::NativeViewAccessible WebView::GetNativeViewAccessible() {
218 if (web_contents()) {
219 content::RenderWidgetHostView* host_view =
220 web_contents()->GetRenderWidgetHostView();
221 if (host_view)
222 return host_view->GetNativeViewAccessible();
224 return View::GetNativeViewAccessible();
227 gfx::Size WebView::GetPreferredSize() const {
228 if (preferred_size_ == gfx::Size())
229 return View::GetPreferredSize();
230 else
231 return preferred_size_;
234 ////////////////////////////////////////////////////////////////////////////////
235 // WebView, content::RenderProcessHostObserver implementation:
237 void WebView::RenderProcessExited(content::RenderProcessHost* host,
238 base::TerminationStatus status,
239 int exit_code) {
240 NotifyMaybeTextInputClientAndAccessibilityChanged();
243 void WebView::RenderProcessHostDestroyed(content::RenderProcessHost* host) {
244 DCHECK_EQ(host, observing_render_process_host_);
245 observing_render_process_host_->RemoveObserver(this);
246 observing_render_process_host_ = nullptr;
249 ////////////////////////////////////////////////////////////////////////////////
250 // WebView, content::WebContentsDelegate implementation:
252 bool WebView::EmbedsFullscreenWidget() const {
253 DCHECK(wc_owner_.get());
254 return embed_fullscreen_widget_mode_enabled_;
257 ////////////////////////////////////////////////////////////////////////////////
258 // WebView, content::WebContentsObserver implementation:
260 void WebView::RenderViewReady() {
261 NotifyMaybeTextInputClientAndAccessibilityChanged();
264 void WebView::RenderViewDeleted(content::RenderViewHost* render_view_host) {
265 NotifyMaybeTextInputClientAndAccessibilityChanged();
268 void WebView::RenderViewHostChanged(content::RenderViewHost* old_host,
269 content::RenderViewHost* new_host) {
270 FocusManager* const focus_manager = GetFocusManager();
271 if (focus_manager && focus_manager->GetFocusedView() == this)
272 OnFocus();
273 NotifyMaybeTextInputClientAndAccessibilityChanged();
276 void WebView::WebContentsDestroyed() {
277 if (observing_render_process_host_) {
278 observing_render_process_host_->RemoveObserver(this);
279 observing_render_process_host_ = nullptr;
281 NotifyMaybeTextInputClientAndAccessibilityChanged();
284 void WebView::DidShowFullscreenWidget(int routing_id) {
285 if (embed_fullscreen_widget_mode_enabled_)
286 ReattachForFullscreenChange(true);
289 void WebView::DidDestroyFullscreenWidget(int routing_id) {
290 if (embed_fullscreen_widget_mode_enabled_)
291 ReattachForFullscreenChange(false);
294 void WebView::DidToggleFullscreenModeForTab(bool entered_fullscreen) {
295 if (embed_fullscreen_widget_mode_enabled_)
296 ReattachForFullscreenChange(entered_fullscreen);
299 void WebView::DidAttachInterstitialPage() {
300 NotifyMaybeTextInputClientAndAccessibilityChanged();
303 void WebView::DidDetachInterstitialPage() {
304 NotifyMaybeTextInputClientAndAccessibilityChanged();
307 void WebView::OnWebContentsFocused() {
308 FocusManager* focus_manager = GetFocusManager();
309 if (focus_manager)
310 focus_manager->SetFocusedView(this);
313 ////////////////////////////////////////////////////////////////////////////////
314 // WebView, private:
316 void WebView::AttachWebContents() {
317 // Prevents attachment if the WebView isn't already in a Widget, or it's
318 // already attached.
319 if (!GetWidget() || !web_contents())
320 return;
322 const gfx::NativeView view_to_attach = is_embedding_fullscreen_widget_ ?
323 web_contents()->GetFullscreenRenderWidgetHostView()->GetNativeView() :
324 web_contents()->GetNativeView();
325 OnBoundsChanged(bounds());
326 if (holder_->native_view() == view_to_attach)
327 return;
329 holder_->Attach(view_to_attach);
331 // The view will not be focused automatically when it is attached, so we need
332 // to pass on focus to it if the FocusManager thinks the view is focused. Note
333 // that not every Widget has a focus manager.
334 FocusManager* const focus_manager = GetFocusManager();
335 if (focus_manager && focus_manager->GetFocusedView() == this)
336 OnFocus();
338 #if defined(OS_WIN)
339 if (!is_embedding_fullscreen_widget_) {
340 web_contents()->SetParentNativeViewAccessible(
341 parent()->GetNativeViewAccessible());
343 #endif
345 OnWebContentsAttached();
348 void WebView::DetachWebContents() {
349 if (web_contents()) {
350 holder_->Detach();
351 #if defined(OS_WIN)
352 if (!is_embedding_fullscreen_widget_)
353 web_contents()->SetParentNativeViewAccessible(NULL);
354 #endif
358 void WebView::ReattachForFullscreenChange(bool enter_fullscreen) {
359 DCHECK(embed_fullscreen_widget_mode_enabled_);
360 const bool web_contents_has_separate_fs_widget =
361 web_contents() && web_contents()->GetFullscreenRenderWidgetHostView();
362 if (is_embedding_fullscreen_widget_ || web_contents_has_separate_fs_widget) {
363 // Shutting down or starting up the embedding of the separate fullscreen
364 // widget. Need to detach and re-attach to a different native view.
365 DetachWebContents();
366 is_embedding_fullscreen_widget_ =
367 enter_fullscreen && web_contents_has_separate_fs_widget;
368 AttachWebContents();
369 } else {
370 // Entering or exiting "non-Flash" fullscreen mode, where the native view is
371 // the same. So, do not change attachment.
372 OnBoundsChanged(bounds());
374 NotifyMaybeTextInputClientAndAccessibilityChanged();
377 void WebView::NotifyMaybeTextInputClientAndAccessibilityChanged() {
378 #if defined(OS_CHROMEOS)
379 if (web_contents())
380 NotifyAccessibilityEvent(ui::AX_EVENT_CHILDREN_CHANGED, false);
381 #endif // defined OS_CHROMEOS
384 content::WebContents* WebView::CreateWebContents(
385 content::BrowserContext* browser_context) {
386 content::WebContents* contents = NULL;
387 if (ViewsDelegate::GetInstance()) {
388 contents =
389 ViewsDelegate::GetInstance()->CreateWebContents(browser_context, NULL);
392 if (!contents) {
393 content::WebContents::CreateParams create_params(
394 browser_context, NULL);
395 return content::WebContents::Create(create_params);
398 return contents;
401 } // namespace views