1 // Copyright 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.
5 #include "chrome/browser/extensions/extension_view_host.h"
7 #include "base/strings/string_piece.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/extensions/extension_system.h"
11 #include "chrome/browser/extensions/window_controller.h"
12 #include "chrome/browser/file_select_helper.h"
13 #include "chrome/browser/platform_util.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_dialogs.h"
16 #include "chrome/common/extensions/extension_messages.h"
17 #include "components/web_modal/web_contents_modal_dialog_manager.h"
18 #include "content/public/browser/notification_source.h"
19 #include "content/public/browser/render_view_host.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/browser/web_contents_view.h"
22 #include "grit/browser_resources.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/events/keycodes/keyboard_codes.h"
26 using content::NativeWebKeyboardEvent
;
27 using content::OpenURLParams
;
28 using content::RenderViewHost
;
29 using content::WebContents
;
30 using content::WebContentsObserver
;
31 using web_modal::WebContentsModalDialogManager
;
33 namespace extensions
{
35 // Notifies an ExtensionViewHost when a WebContents is destroyed.
36 class ExtensionViewHost::AssociatedWebContentsObserver
37 : public WebContentsObserver
{
39 AssociatedWebContentsObserver(ExtensionViewHost
* host
,
40 WebContents
* web_contents
)
41 : WebContentsObserver(web_contents
), host_(host
) {}
42 virtual ~AssociatedWebContentsObserver() {}
44 // content::WebContentsObserver:
45 virtual void WebContentsDestroyed(WebContents
* web_contents
) OVERRIDE
{
46 // Deleting |this| from here is safe.
47 host_
->SetAssociatedWebContents(NULL
);
51 ExtensionViewHost
* host_
;
53 DISALLOW_COPY_AND_ASSIGN(AssociatedWebContentsObserver
);
56 ExtensionViewHost::ExtensionViewHost(
57 const Extension
* extension
,
58 content::SiteInstance
* site_instance
,
61 : ExtensionHost(extension
, site_instance
, url
, host_type
),
62 associated_web_contents_(NULL
) {
63 // Not used for panels, see PanelHost.
64 DCHECK(host_type
== VIEW_TYPE_EXTENSION_DIALOG
||
65 host_type
== VIEW_TYPE_EXTENSION_INFOBAR
||
66 host_type
== VIEW_TYPE_EXTENSION_POPUP
);
69 ExtensionViewHost::~ExtensionViewHost() {
70 // The hosting WebContents will be deleted in the base class, so unregister
71 // this object before it deletes the attached WebContentsModalDialogManager.
72 WebContentsModalDialogManager
* manager
=
73 WebContentsModalDialogManager::FromWebContents(host_contents());
75 manager
->SetDelegate(NULL
);
78 void ExtensionViewHost::CreateView(Browser
* browser
) {
79 #if defined(TOOLKIT_VIEWS)
80 view_
.reset(new ExtensionViewViews(this, browser
));
81 // We own |view_|, so don't auto delete when it's removed from the view
83 view_
->set_owned_by_client();
84 #elif defined(OS_MACOSX)
85 view_
.reset(new ExtensionViewMac(this, browser
));
87 #elif defined(TOOLKIT_GTK)
88 view_
.reset(new ExtensionViewGtk(this, browser
));
96 void ExtensionViewHost::SetAssociatedWebContents(WebContents
* web_contents
) {
97 associated_web_contents_
= web_contents
;
98 if (associated_web_contents_
) {
99 // Observe the new WebContents for deletion.
100 associated_web_contents_observer_
.reset(
101 new AssociatedWebContentsObserver(this, associated_web_contents_
));
103 associated_web_contents_observer_
.reset();
107 void ExtensionViewHost::UnhandledKeyboardEvent(
109 const content::NativeWebKeyboardEvent
& event
) {
110 Browser
* browser
= view_
->browser();
112 // Handle lower priority browser shortcuts such as Ctrl-f.
113 return browser
->HandleKeyboardEvent(source
, event
);
115 #if defined(TOOLKIT_VIEWS)
116 // In case there's no Browser (e.g. for dialogs), pass it to
117 // ExtensionViewViews to handle accelerators. The view's FocusManager does
118 // not know anything about Browser accelerators, but might know others such
120 view_
->HandleKeyboardEvent(event
);
125 // ExtensionHost overrides:
127 void ExtensionViewHost::OnDidStopLoading() {
128 DCHECK(did_stop_loading());
129 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
130 view_
->DidStopLoading();
134 void ExtensionViewHost::OnDocumentAvailable() {
135 if (extension_host_type() == VIEW_TYPE_EXTENSION_INFOBAR
) {
136 // No style sheet for other types, at the moment.
141 void ExtensionViewHost::LoadInitialURL() {
142 if (!ExtensionSystem::GetForBrowserContext(browser_context())->
143 extension_service()->IsBackgroundPageReady(extension())) {
144 // Make sure the background page loads before any others.
145 registrar()->Add(this,
146 chrome::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY
,
147 content::Source
<Extension
>(extension()));
151 // Popups may spawn modal dialogs, which need positioning information.
152 if (extension_host_type() == VIEW_TYPE_EXTENSION_POPUP
) {
153 WebContentsModalDialogManager::CreateForWebContents(host_contents());
154 WebContentsModalDialogManager::FromWebContents(
155 host_contents())->SetDelegate(this);
158 ExtensionHost::LoadInitialURL();
161 bool ExtensionViewHost::IsBackgroundPage() const {
166 // content::WebContentsDelegate overrides:
168 WebContents
* ExtensionViewHost::OpenURLFromTab(
170 const OpenURLParams
& params
) {
171 // Whitelist the dispositions we will allow to be opened.
172 switch (params
.disposition
) {
174 case NEW_FOREGROUND_TAB
:
175 case NEW_BACKGROUND_TAB
:
179 case OFF_THE_RECORD
: {
180 // Only allow these from hosts that are bound to a browser (e.g. popups).
181 // Otherwise they are not driven by a user gesture.
182 Browser
* browser
= view_
->browser();
183 return browser
? browser
->OpenURL(params
) : NULL
;
190 bool ExtensionViewHost::PreHandleKeyboardEvent(
192 const NativeWebKeyboardEvent
& event
,
193 bool* is_keyboard_shortcut
) {
194 if (extension_host_type() == VIEW_TYPE_EXTENSION_POPUP
&&
195 event
.type
== NativeWebKeyboardEvent::RawKeyDown
&&
196 event
.windowsKeyCode
== ui::VKEY_ESCAPE
) {
197 DCHECK(is_keyboard_shortcut
!= NULL
);
198 *is_keyboard_shortcut
= true;
202 // Handle higher priority browser shortcuts such as Ctrl-w.
203 Browser
* browser
= view_
->browser();
205 return browser
->PreHandleKeyboardEvent(source
, event
, is_keyboard_shortcut
);
207 *is_keyboard_shortcut
= false;
211 void ExtensionViewHost::HandleKeyboardEvent(
213 const NativeWebKeyboardEvent
& event
) {
214 if (extension_host_type() == VIEW_TYPE_EXTENSION_POPUP
) {
215 if (event
.type
== NativeWebKeyboardEvent::RawKeyDown
&&
216 event
.windowsKeyCode
== ui::VKEY_ESCAPE
) {
221 UnhandledKeyboardEvent(source
, event
);
224 content::ColorChooser
* ExtensionViewHost::OpenColorChooser(
225 WebContents
* web_contents
,
226 SkColor initial_color
,
227 const std::vector
<content::ColorSuggestion
>& suggestions
) {
228 // Similar to the file chooser below, opening a color chooser requires a
229 // visible <input> element to click on. Therefore this code only exists for
230 // extensions with a view.
231 return chrome::ShowColorChooser(web_contents
, initial_color
);
234 void ExtensionViewHost::RunFileChooser(
236 const content::FileChooserParams
& params
) {
237 // For security reasons opening a file picker requires a visible <input>
238 // element to click on, so this code only exists for extensions with a view.
239 FileSelectHelper::RunFileChooser(tab
, params
);
243 void ExtensionViewHost::ResizeDueToAutoResize(WebContents
* source
,
244 const gfx::Size
& new_size
) {
245 view_
->ResizeDueToAutoResize(new_size
);
248 // content::WebContentsObserver overrides:
250 void ExtensionViewHost::RenderViewCreated(RenderViewHost
* render_view_host
) {
251 ExtensionHost::RenderViewCreated(render_view_host
);
253 view_
->RenderViewCreated();
255 // If the host is bound to a window, then extract its id. Extensions hosted
256 // in ExternalTabContainer objects may not have an associated window.
257 WindowController
* window
= GetExtensionWindowController();
259 render_view_host
->Send(new ExtensionMsg_UpdateBrowserWindowId(
260 render_view_host
->GetRoutingID(), window
->GetWindowId()));
264 // web_modal::WebContentsModalDialogManagerDelegate overrides:
266 web_modal::WebContentsModalDialogHost
*
267 ExtensionViewHost::GetWebContentsModalDialogHost() {
271 bool ExtensionViewHost::IsWebContentsVisible(WebContents
* web_contents
) {
272 return platform_util::IsVisible(web_contents
->GetView()->GetNativeView());
275 gfx::NativeView
ExtensionViewHost::GetHostView() const {
276 return view_
->native_view();
279 gfx::Point
ExtensionViewHost::GetDialogPosition(const gfx::Size
& size
) {
280 if (!GetVisibleWebContents())
282 gfx::Rect bounds
= GetVisibleWebContents()->GetView()->GetViewBounds();
284 std::max(0, (bounds
.width() - size
.width()) / 2),
285 std::max(0, (bounds
.height() - size
.height()) / 2));
288 gfx::Size
ExtensionViewHost::GetMaximumDialogSize() {
289 if (!GetVisibleWebContents())
291 return GetVisibleWebContents()->GetView()->GetViewBounds().size();
294 void ExtensionViewHost::AddObserver(
295 web_modal::ModalDialogHostObserver
* observer
) {
298 void ExtensionViewHost::RemoveObserver(
299 web_modal::ModalDialogHostObserver
* observer
) {
302 WindowController
* ExtensionViewHost::GetExtensionWindowController() const {
303 return view_
->browser() ? view_
->browser()->extension_window_controller()
307 WebContents
* ExtensionViewHost::GetAssociatedWebContents() const {
308 return associated_web_contents_
;
311 WebContents
* ExtensionViewHost::GetVisibleWebContents() const {
312 if (associated_web_contents_
)
313 return associated_web_contents_
;
314 if (extension_host_type() == VIEW_TYPE_EXTENSION_POPUP
)
315 return host_contents();
319 void ExtensionViewHost::Observe(int type
,
320 const content::NotificationSource
& source
,
321 const content::NotificationDetails
& details
) {
322 if (type
== chrome::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY
) {
323 DCHECK(ExtensionSystem::GetForBrowserContext(browser_context())->
324 extension_service()->IsBackgroundPageReady(extension()));
328 ExtensionHost::Observe(type
, source
, details
);
331 void ExtensionViewHost::InsertInfobarCSS() {
332 static const base::StringPiece
css(
333 ResourceBundle::GetSharedInstance().GetRawDataResource(
334 IDR_EXTENSIONS_INFOBAR_CSS
));
336 render_view_host()->InsertCSS(base::string16(), css
.as_string());
339 } // namespace extensions