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.
4
5 #include "chrome/browser/extensions/extension_view_host.h"
6
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"
25
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;
32
33 namespace extensions {
34
35 // Notifies an ExtensionViewHost when a WebContents is destroyed.
36 class ExtensionViewHost::AssociatedWebContentsObserver
37 : public WebContentsObserver {
38 public:
AssociatedWebContentsObserver(ExtensionViewHost * host,WebContents * web_contents)39 AssociatedWebContentsObserver(ExtensionViewHost* host,
40 WebContents* web_contents)
41 : WebContentsObserver(web_contents), host_(host) {}
~AssociatedWebContentsObserver()42 virtual ~AssociatedWebContentsObserver() {}
43
44 // content::WebContentsObserver:
WebContentsDestroyed(WebContents * web_contents)45 virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE {
46 // Deleting |this| from here is safe.
47 host_->SetAssociatedWebContents(NULL);
48 }
49
50 private:
51 ExtensionViewHost* host_;
52
53 DISALLOW_COPY_AND_ASSIGN(AssociatedWebContentsObserver);
54 };
55
ExtensionViewHost(const Extension * extension,content::SiteInstance * site_instance,const GURL & url,ViewType host_type)56 ExtensionViewHost::ExtensionViewHost(
57 const Extension* extension,
58 content::SiteInstance* site_instance,
59 const GURL& url,
60 ViewType host_type)
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);
67 }
68
~ExtensionViewHost()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());
74 if (manager)
75 manager->SetDelegate(NULL);
76 }
77
CreateView(Browser * browser)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
82 // hierarchy.
83 view_->set_owned_by_client();
84 #elif defined(OS_MACOSX)
85 view_.reset(new ExtensionViewMac(this, browser));
86 view_->Init();
87 #elif defined(TOOLKIT_GTK)
88 view_.reset(new ExtensionViewGtk(this, browser));
89 view_->Init();
90 #else
91 // TODO(port)
92 NOTREACHED();
93 #endif
94 }
95
SetAssociatedWebContents(WebContents * web_contents)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_));
102 } else {
103 associated_web_contents_observer_.reset();
104 }
105 }
106
UnhandledKeyboardEvent(WebContents * source,const content::NativeWebKeyboardEvent & event)107 void ExtensionViewHost::UnhandledKeyboardEvent(
108 WebContents* source,
109 const content::NativeWebKeyboardEvent& event) {
110 Browser* browser = view_->browser();
111 if (browser) {
112 // Handle lower priority browser shortcuts such as Ctrl-f.
113 return browser->HandleKeyboardEvent(source, event);
114 } else {
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
119 // as Ash's.
120 view_->HandleKeyboardEvent(event);
121 #endif
122 }
123 }
124
125 // ExtensionHost overrides:
126
OnDidStopLoading()127 void ExtensionViewHost::OnDidStopLoading() {
128 DCHECK(did_stop_loading());
129 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
130 view_->DidStopLoading();
131 #endif
132 }
133
OnDocumentAvailable()134 void ExtensionViewHost::OnDocumentAvailable() {
135 if (extension_host_type() == VIEW_TYPE_EXTENSION_INFOBAR) {
136 // No style sheet for other types, at the moment.
137 InsertInfobarCSS();
138 }
139 }
140
LoadInitialURL()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()));
148 return;
149 }
150
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);
156 }
157
158 ExtensionHost::LoadInitialURL();
159 }
160
IsBackgroundPage() const161 bool ExtensionViewHost::IsBackgroundPage() const {
162 DCHECK(view_);
163 return false;
164 }
165
166 // content::WebContentsDelegate overrides:
167
OpenURLFromTab(WebContents * source,const OpenURLParams & params)168 WebContents* ExtensionViewHost::OpenURLFromTab(
169 WebContents* source,
170 const OpenURLParams& params) {
171 // Whitelist the dispositions we will allow to be opened.
172 switch (params.disposition) {
173 case SINGLETON_TAB:
174 case NEW_FOREGROUND_TAB:
175 case NEW_BACKGROUND_TAB:
176 case NEW_POPUP:
177 case NEW_WINDOW:
178 case SAVE_TO_DISK:
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;
184 }
185 default:
186 return NULL;
187 }
188 }
189
PreHandleKeyboardEvent(WebContents * source,const NativeWebKeyboardEvent & event,bool * is_keyboard_shortcut)190 bool ExtensionViewHost::PreHandleKeyboardEvent(
191 WebContents* source,
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;
199 return false;
200 }
201
202 // Handle higher priority browser shortcuts such as Ctrl-w.
203 Browser* browser = view_->browser();
204 if (browser)
205 return browser->PreHandleKeyboardEvent(source, event, is_keyboard_shortcut);
206
207 *is_keyboard_shortcut = false;
208 return false;
209 }
210
HandleKeyboardEvent(WebContents * source,const NativeWebKeyboardEvent & event)211 void ExtensionViewHost::HandleKeyboardEvent(
212 WebContents* source,
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) {
217 Close();
218 return;
219 }
220 }
221 UnhandledKeyboardEvent(source, event);
222 }
223
OpenColorChooser(WebContents * web_contents,SkColor initial_color,const std::vector<content::ColorSuggestion> & suggestions)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);
232 }
233
RunFileChooser(WebContents * tab,const content::FileChooserParams & params)234 void ExtensionViewHost::RunFileChooser(
235 WebContents* tab,
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);
240 }
241
242
ResizeDueToAutoResize(WebContents * source,const gfx::Size & new_size)243 void ExtensionViewHost::ResizeDueToAutoResize(WebContents* source,
244 const gfx::Size& new_size) {
245 view_->ResizeDueToAutoResize(new_size);
246 }
247
248 // content::WebContentsObserver overrides:
249
RenderViewCreated(RenderViewHost * render_view_host)250 void ExtensionViewHost::RenderViewCreated(RenderViewHost* render_view_host) {
251 ExtensionHost::RenderViewCreated(render_view_host);
252
253 view_->RenderViewCreated();
254
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();
258 if (window) {
259 render_view_host->Send(new ExtensionMsg_UpdateBrowserWindowId(
260 render_view_host->GetRoutingID(), window->GetWindowId()));
261 }
262 }
263
264 // web_modal::WebContentsModalDialogManagerDelegate overrides:
265
266 web_modal::WebContentsModalDialogHost*
GetWebContentsModalDialogHost()267 ExtensionViewHost::GetWebContentsModalDialogHost() {
268 return this;
269 }
270
IsWebContentsVisible(WebContents * web_contents)271 bool ExtensionViewHost::IsWebContentsVisible(WebContents* web_contents) {
272 return platform_util::IsVisible(web_contents->GetView()->GetNativeView());
273 }
274
GetHostView() const275 gfx::NativeView ExtensionViewHost::GetHostView() const {
276 return view_->native_view();
277 }
278
GetDialogPosition(const gfx::Size & size)279 gfx::Point ExtensionViewHost::GetDialogPosition(const gfx::Size& size) {
280 if (!GetVisibleWebContents())
281 return gfx::Point();
282 gfx::Rect bounds = GetVisibleWebContents()->GetView()->GetViewBounds();
283 return gfx::Point(
284 std::max(0, (bounds.width() - size.width()) / 2),
285 std::max(0, (bounds.height() - size.height()) / 2));
286 }
287
GetMaximumDialogSize()288 gfx::Size ExtensionViewHost::GetMaximumDialogSize() {
289 if (!GetVisibleWebContents())
290 return gfx::Size();
291 return GetVisibleWebContents()->GetView()->GetViewBounds().size();
292 }
293
AddObserver(web_modal::ModalDialogHostObserver * observer)294 void ExtensionViewHost::AddObserver(
295 web_modal::ModalDialogHostObserver* observer) {
296 }
297
RemoveObserver(web_modal::ModalDialogHostObserver * observer)298 void ExtensionViewHost::RemoveObserver(
299 web_modal::ModalDialogHostObserver* observer) {
300 }
301
GetExtensionWindowController() const302 WindowController* ExtensionViewHost::GetExtensionWindowController() const {
303 return view_->browser() ? view_->browser()->extension_window_controller()
304 : NULL;
305 }
306
GetAssociatedWebContents() const307 WebContents* ExtensionViewHost::GetAssociatedWebContents() const {
308 return associated_web_contents_;
309 }
310
GetVisibleWebContents() const311 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();
316 return NULL;
317 }
318
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)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()));
325 LoadInitialURL();
326 return;
327 }
328 ExtensionHost::Observe(type, source, details);
329 }
330
InsertInfobarCSS()331 void ExtensionViewHost::InsertInfobarCSS() {
332 static const base::StringPiece css(
333 ResourceBundle::GetSharedInstance().GetRawDataResource(
334 IDR_EXTENSIONS_INFOBAR_CSS));
335
336 render_view_host()->InsertCSS(base::string16(), css.as_string());
337 }
338
339 } // namespace extensions
340