1 // Copyright 2014 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.
6 #include "chrome/browser/guest_view/web_view/chrome_web_view_guest_delegate.h"
8 #include "chrome/browser/extensions/api/web_request/web_request_api.h"
9 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
10 #include "chrome/browser/favicon/favicon_tab_helper.h"
11 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
12 #include "chrome/browser/ui/pdf/chrome_pdf_web_contents_helper_client.h"
13 #include "chrome/browser/ui/zoom/zoom_controller.h"
14 #include "chrome/common/chrome_version_info.h"
15 #include "components/pdf/browser/pdf_web_contents_helper.h"
16 #include "components/renderer_context_menu/context_menu_delegate.h"
17 #include "content/public/common/page_zoom.h"
18 #include "extensions/browser/guest_view/web_view/web_view_constants.h"
20 #if defined(ENABLE_PRINTING)
21 #if defined(ENABLE_FULL_PRINTING)
22 #include "chrome/browser/printing/print_preview_message_handler.h"
23 #include "chrome/browser/printing/print_view_manager.h"
25 #include "chrome/browser/printing/print_view_manager_basic.h"
26 #endif // defined(ENABLE_FULL_PRINTING)
27 #endif // defined(ENABLE_PRINTING)
29 void RemoveWebViewEventListenersOnIOThread(
31 const std::string
& extension_id
,
32 int embedder_process_id
,
33 int view_instance_id
) {
34 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
35 ExtensionWebRequestEventRouter::GetInstance()->RemoveWebViewEventListeners(
42 ChromeWebViewGuestDelegate::ChromeWebViewGuestDelegate(
43 extensions::WebViewGuest
* web_view_guest
)
44 : WebViewGuestDelegate(web_view_guest
),
45 find_helper_(web_view_guest
),
46 pending_context_menu_request_id_(0),
47 chromevox_injected_(false),
48 current_zoom_factor_(1.0) {
51 ChromeWebViewGuestDelegate::~ChromeWebViewGuestDelegate() {
54 void ChromeWebViewGuestDelegate::Find(
55 const base::string16
& search_text
,
56 const blink::WebFindOptions
& options
,
57 extensions::WebViewInternalFindFunction
* find_function
) {
58 find_helper_
.Find(guest_web_contents(), search_text
, options
, find_function
);
61 void ChromeWebViewGuestDelegate::FindReply(content::WebContents
* source
,
63 int number_of_matches
,
64 const gfx::Rect
& selection_rect
,
65 int active_match_ordinal
,
67 find_helper_
.FindReply(request_id
, number_of_matches
, selection_rect
,
68 active_match_ordinal
, final_update
);
71 void ChromeWebViewGuestDelegate::StopFinding(content::StopFindAction action
) {
72 find_helper_
.CancelAllFindSessions();
73 guest_web_contents()->StopFinding(action
);
76 double ChromeWebViewGuestDelegate::GetZoom() {
77 return current_zoom_factor_
;
80 bool ChromeWebViewGuestDelegate::HandleContextMenu(
81 const content::ContextMenuParams
& params
) {
82 ContextMenuDelegate
* menu_delegate
=
83 ContextMenuDelegate::FromWebContents(guest_web_contents());
84 DCHECK(menu_delegate
);
86 pending_menu_
= menu_delegate
->BuildMenu(guest_web_contents(), params
);
88 // Pass it to embedder.
89 int request_id
= ++pending_context_menu_request_id_
;
90 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
91 scoped_ptr
<base::ListValue
> items
=
92 MenuModelToValue(pending_menu_
->menu_model());
93 args
->Set(webview::kContextMenuItems
, items
.release());
94 args
->SetInteger(webview::kRequestId
, request_id
);
95 web_view_guest()->DispatchEventToEmbedder(
96 new extensions::GuestViewBase::Event(
97 webview::kEventContextMenu
, args
.Pass()));
101 // TODO(hanxi) Investigate which of these observers should move to the
102 // extension module in the future.
103 void ChromeWebViewGuestDelegate::OnAttachWebViewHelpers(
104 content::WebContents
* contents
) {
105 // Create a zoom controller for the guest contents give it access to
106 // GetZoomLevel() and and SetZoomLevel() in WebViewGuest.
107 // TODO(wjmaclean) This currently uses the same HostZoomMap as the browser
108 // context, but we eventually want to isolate the guest contents from zoom
109 // changes outside the guest (e.g. in the main browser), so we should
110 // create a separate HostZoomMap for the guest.
111 ZoomController::CreateForWebContents(contents
);
113 FaviconTabHelper::CreateForWebContents(contents
);
114 extensions::ChromeExtensionWebContentsObserver::
115 CreateForWebContents(contents
);
116 #if defined(ENABLE_PRINTING)
117 #if defined(ENABLE_FULL_PRINTING)
118 printing::PrintViewManager::CreateForWebContents(contents
);
119 printing::PrintPreviewMessageHandler::CreateForWebContents(contents
);
121 printing::PrintViewManagerBasic::CreateForWebContents(contents
);
122 #endif // defined(ENABLE_FULL_PRINTING)
123 #endif // defined(ENABLE_PRINTING)
124 pdf::PDFWebContentsHelper::CreateForWebContentsWithClient(
126 scoped_ptr
<pdf::PDFWebContentsHelperClient
>(
127 new ChromePDFWebContentsHelperClient()));
130 void ChromeWebViewGuestDelegate::OnEmbedderDestroyed() {
131 // TODO(fsamuel): WebRequest event listeners for <webview> should survive
132 // reparenting of a <webview> within a single embedder. Right now, we keep
133 // around the browser state for the listener for the lifetime of the embedder.
134 // Ideally, the lifetime of the listeners should match the lifetime of the
135 // <webview> DOM node. Once http://crbug.com/156219 is resolved we can move
136 // the call to RemoveWebViewEventListenersOnIOThread back to
137 // WebViewGuest::WebContentsDestroyed.
138 content::BrowserThread::PostTask(
139 content::BrowserThread::IO
,
142 &RemoveWebViewEventListenersOnIOThread
,
143 web_view_guest()->browser_context(),
144 web_view_guest()->embedder_extension_id(),
145 web_view_guest()->embedder_render_process_id(),
146 web_view_guest()->view_instance_id()));
149 void ChromeWebViewGuestDelegate::OnDidCommitProvisionalLoadForFrame(
150 bool is_main_frame
) {
151 find_helper_
.CancelAllFindSessions();
153 // Update the current zoom factor for the new page.
154 ZoomController
* zoom_controller
=
155 ZoomController::FromWebContents(guest_web_contents());
156 DCHECK(zoom_controller
);
157 current_zoom_factor_
= zoom_controller
->GetZoomLevel();
159 chromevox_injected_
= false;
162 void ChromeWebViewGuestDelegate::OnDidInitialize() {
163 #if defined(OS_CHROMEOS)
164 chromeos::AccessibilityManager
* accessibility_manager
=
165 chromeos::AccessibilityManager::Get();
166 CHECK(accessibility_manager
);
167 accessibility_subscription_
= accessibility_manager
->RegisterCallback(
168 base::Bind(&ChromeWebViewGuestDelegate::OnAccessibilityStatusChanged
,
169 base::Unretained(this)));
173 void ChromeWebViewGuestDelegate::OnDocumentLoadedInFrame(
174 content::RenderFrameHost
* render_frame_host
) {
175 if (!render_frame_host
->GetParent())
176 InjectChromeVoxIfNeeded(render_frame_host
->GetRenderViewHost());
179 void ChromeWebViewGuestDelegate::OnGuestDestroyed() {
180 // Clean up custom context menu items for this guest.
181 extensions::MenuManager
* menu_manager
= extensions::MenuManager::Get(
182 Profile::FromBrowserContext(web_view_guest()->browser_context()));
183 menu_manager
->RemoveAllContextItems(extensions::MenuItem::ExtensionKey(
184 web_view_guest()->embedder_extension_id(),
185 web_view_guest()->view_instance_id()));
189 scoped_ptr
<base::ListValue
> ChromeWebViewGuestDelegate::MenuModelToValue(
190 const ui::SimpleMenuModel
& menu_model
) {
191 scoped_ptr
<base::ListValue
> items(new base::ListValue());
192 for (int i
= 0; i
< menu_model
.GetItemCount(); ++i
) {
193 base::DictionaryValue
* item_value
= new base::DictionaryValue();
194 // TODO(lazyboy): We need to expose some kind of enum equivalent of
195 // |command_id| instead of plain integers.
196 item_value
->SetInteger(webview::kMenuItemCommandId
,
197 menu_model
.GetCommandIdAt(i
));
198 item_value
->SetString(webview::kMenuItemLabel
, menu_model
.GetLabelAt(i
));
199 items
->Append(item_value
);
204 void ChromeWebViewGuestDelegate::OnRenderProcessGone() {
205 // Cancel all find sessions in progress.
206 find_helper_
.CancelAllFindSessions();
209 void ChromeWebViewGuestDelegate::OnSetZoom(double zoom_factor
) {
210 ZoomController
* zoom_controller
=
211 ZoomController::FromWebContents(guest_web_contents());
212 DCHECK(zoom_controller
);
213 double zoom_level
= content::ZoomFactorToZoomLevel(zoom_factor
);
214 zoom_controller
->SetZoomLevel(zoom_level
);
216 scoped_ptr
<base::DictionaryValue
> args(new base::DictionaryValue());
217 args
->SetDouble(webview::kOldZoomFactor
, current_zoom_factor_
);
218 args
->SetDouble(webview::kNewZoomFactor
, zoom_factor
);
219 web_view_guest()->DispatchEventToEmbedder(
220 new extensions::GuestViewBase::Event(
221 webview::kEventZoomChange
, args
.Pass()));
222 current_zoom_factor_
= zoom_factor
;
225 void ChromeWebViewGuestDelegate::OnShowContextMenu(
227 const MenuItemVector
* items
) {
228 if (!pending_menu_
.get())
231 // Make sure this was the correct request.
232 if (request_id
!= pending_context_menu_request_id_
)
235 // TODO(lazyboy): Implement.
238 ContextMenuDelegate
* menu_delegate
=
239 ContextMenuDelegate::FromWebContents(guest_web_contents());
240 menu_delegate
->ShowMenu(pending_menu_
.Pass());
243 void ChromeWebViewGuestDelegate::InjectChromeVoxIfNeeded(
244 content::RenderViewHost
* render_view_host
) {
245 #if defined(OS_CHROMEOS)
246 if (!chromevox_injected_
) {
247 chromeos::AccessibilityManager
* manager
=
248 chromeos::AccessibilityManager::Get();
249 if (manager
&& manager
->IsSpokenFeedbackEnabled()) {
250 manager
->InjectChromeVox(render_view_host
);
251 chromevox_injected_
= true;
257 #if defined(OS_CHROMEOS)
258 void ChromeWebViewGuestDelegate::OnAccessibilityStatusChanged(
259 const chromeos::AccessibilityStatusEventDetails
& details
) {
260 if (details
.notification_type
== chromeos::ACCESSIBILITY_MANAGER_SHUTDOWN
) {
261 accessibility_subscription_
.reset();
262 } else if (details
.notification_type
==
263 chromeos::ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK
) {
265 InjectChromeVoxIfNeeded(guest_web_contents()->GetRenderViewHost());
267 chromevox_injected_
= false;