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_view.h"
10 #include "chrome/browser/extensions/window_controller.h"
11 #include "chrome/browser/file_select_helper.h"
12 #include "chrome/browser/platform_util.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/browser_dialogs.h"
15 #include "components/web_modal/web_contents_modal_dialog_manager.h"
16 #include "content/public/browser/notification_source.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "extensions/browser/extension_system.h"
20 #include "extensions/browser/runtime_data.h"
21 #include "extensions/common/extension_messages.h"
22 #include "grit/browser_resources.h"
23 #include "third_party/WebKit/public/web/WebInputEvent.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/events/keycodes/keyboard_codes.h"
26
27 using content::NativeWebKeyboardEvent;
28 using content::OpenURLParams;
29 using content::RenderViewHost;
30 using content::WebContents;
31 using content::WebContentsObserver;
32 using web_modal::WebContentsModalDialogManager;
33
34 namespace extensions {
35
36 // Notifies an ExtensionViewHost when a WebContents is destroyed.
37 class ExtensionViewHost::AssociatedWebContentsObserver
38 : public WebContentsObserver {
39 public:
AssociatedWebContentsObserver(ExtensionViewHost * host,WebContents * web_contents)40 AssociatedWebContentsObserver(ExtensionViewHost* host,
41 WebContents* web_contents)
42 : WebContentsObserver(web_contents), host_(host) {}
~AssociatedWebContentsObserver()43 virtual ~AssociatedWebContentsObserver() {}
44
45 // content::WebContentsObserver:
WebContentsDestroyed()46 virtual void WebContentsDestroyed() OVERRIDE {
47 // Deleting |this| from here is safe.
48 host_->SetAssociatedWebContents(NULL);
49 }
50
51 private:
52 ExtensionViewHost* host_;
53
54 DISALLOW_COPY_AND_ASSIGN(AssociatedWebContentsObserver);
55 };
56
ExtensionViewHost(const Extension * extension,content::SiteInstance * site_instance,const GURL & url,ViewType host_type)57 ExtensionViewHost::ExtensionViewHost(
58 const Extension* extension,
59 content::SiteInstance* site_instance,
60 const GURL& url,
61 ViewType host_type)
62 : ExtensionHost(extension, site_instance, url, host_type),
63 associated_web_contents_(NULL) {
64 // Not used for panels, see PanelHost.
65 DCHECK(host_type == VIEW_TYPE_EXTENSION_DIALOG ||
66 host_type == VIEW_TYPE_EXTENSION_INFOBAR ||
67 host_type == VIEW_TYPE_EXTENSION_POPUP);
68 }
69
~ExtensionViewHost()70 ExtensionViewHost::~ExtensionViewHost() {
71 // The hosting WebContents will be deleted in the base class, so unregister
72 // this object before it deletes the attached WebContentsModalDialogManager.
73 WebContentsModalDialogManager* manager =
74 WebContentsModalDialogManager::FromWebContents(host_contents());
75 if (manager)
76 manager->SetDelegate(NULL);
77 }
78
CreateView(Browser * browser)79 void ExtensionViewHost::CreateView(Browser* browser) {
80 view_ = CreateExtensionView(this, browser);
81 view_->Init();
82 }
83
SetAssociatedWebContents(WebContents * web_contents)84 void ExtensionViewHost::SetAssociatedWebContents(WebContents* web_contents) {
85 associated_web_contents_ = web_contents;
86 if (associated_web_contents_) {
87 // Observe the new WebContents for deletion.
88 associated_web_contents_observer_.reset(
89 new AssociatedWebContentsObserver(this, associated_web_contents_));
90 } else {
91 associated_web_contents_observer_.reset();
92 }
93 }
94
UnhandledKeyboardEvent(WebContents * source,const content::NativeWebKeyboardEvent & event)95 void ExtensionViewHost::UnhandledKeyboardEvent(
96 WebContents* source,
97 const content::NativeWebKeyboardEvent& event) {
98 view_->HandleKeyboardEvent(source, event);
99 }
100
101 // ExtensionHost overrides:
102
OnDidStopLoading()103 void ExtensionViewHost::OnDidStopLoading() {
104 DCHECK(did_stop_loading());
105 view_->DidStopLoading();
106 }
107
OnDocumentAvailable()108 void ExtensionViewHost::OnDocumentAvailable() {
109 if (extension_host_type() == VIEW_TYPE_EXTENSION_INFOBAR) {
110 // No style sheet for other types, at the moment.
111 InsertInfobarCSS();
112 }
113 }
114
LoadInitialURL()115 void ExtensionViewHost::LoadInitialURL() {
116 if (!ExtensionSystem::Get(browser_context())->
117 runtime_data()->IsBackgroundPageReady(extension())) {
118 // Make sure the background page loads before any others.
119 registrar()->Add(this,
120 extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY,
121 content::Source<Extension>(extension()));
122 return;
123 }
124
125 // Popups may spawn modal dialogs, which need positioning information.
126 if (extension_host_type() == VIEW_TYPE_EXTENSION_POPUP) {
127 WebContentsModalDialogManager::CreateForWebContents(host_contents());
128 WebContentsModalDialogManager::FromWebContents(
129 host_contents())->SetDelegate(this);
130 if (!popup_manager_.get())
131 popup_manager_.reset(new web_modal::PopupManager(this));
132 popup_manager_->RegisterWith(host_contents());
133 }
134
135 ExtensionHost::LoadInitialURL();
136 }
137
IsBackgroundPage() const138 bool ExtensionViewHost::IsBackgroundPage() const {
139 DCHECK(view_);
140 return false;
141 }
142
143 // content::WebContentsDelegate overrides:
144
OpenURLFromTab(WebContents * source,const OpenURLParams & params)145 WebContents* ExtensionViewHost::OpenURLFromTab(
146 WebContents* source,
147 const OpenURLParams& params) {
148 // Whitelist the dispositions we will allow to be opened.
149 switch (params.disposition) {
150 case SINGLETON_TAB:
151 case NEW_FOREGROUND_TAB:
152 case NEW_BACKGROUND_TAB:
153 case NEW_POPUP:
154 case NEW_WINDOW:
155 case SAVE_TO_DISK:
156 case OFF_THE_RECORD: {
157 // Only allow these from hosts that are bound to a browser (e.g. popups).
158 // Otherwise they are not driven by a user gesture.
159 Browser* browser = view_->GetBrowser();
160 return browser ? browser->OpenURL(params) : NULL;
161 }
162 default:
163 return NULL;
164 }
165 }
166
PreHandleKeyboardEvent(WebContents * source,const NativeWebKeyboardEvent & event,bool * is_keyboard_shortcut)167 bool ExtensionViewHost::PreHandleKeyboardEvent(
168 WebContents* source,
169 const NativeWebKeyboardEvent& event,
170 bool* is_keyboard_shortcut) {
171 if (extension_host_type() == VIEW_TYPE_EXTENSION_POPUP &&
172 event.type == NativeWebKeyboardEvent::RawKeyDown &&
173 event.windowsKeyCode == ui::VKEY_ESCAPE) {
174 DCHECK(is_keyboard_shortcut != NULL);
175 *is_keyboard_shortcut = true;
176 return false;
177 }
178
179 // Handle higher priority browser shortcuts such as Ctrl-w.
180 Browser* browser = view_->GetBrowser();
181 if (browser)
182 return browser->PreHandleKeyboardEvent(source, event, is_keyboard_shortcut);
183
184 *is_keyboard_shortcut = false;
185 return false;
186 }
187
HandleKeyboardEvent(WebContents * source,const NativeWebKeyboardEvent & event)188 void ExtensionViewHost::HandleKeyboardEvent(
189 WebContents* source,
190 const NativeWebKeyboardEvent& event) {
191 if (extension_host_type() == VIEW_TYPE_EXTENSION_POPUP) {
192 if (event.type == NativeWebKeyboardEvent::RawKeyDown &&
193 event.windowsKeyCode == ui::VKEY_ESCAPE) {
194 Close();
195 return;
196 }
197 }
198 UnhandledKeyboardEvent(source, event);
199 }
200
PreHandleGestureEvent(content::WebContents * source,const blink::WebGestureEvent & event)201 bool ExtensionViewHost::PreHandleGestureEvent(
202 content::WebContents* source,
203 const blink::WebGestureEvent& event) {
204 // Disable pinch zooming.
205 return event.type == blink::WebGestureEvent::GesturePinchBegin ||
206 event.type == blink::WebGestureEvent::GesturePinchUpdate ||
207 event.type == blink::WebGestureEvent::GesturePinchEnd;
208 }
209
OpenColorChooser(WebContents * web_contents,SkColor initial_color,const std::vector<content::ColorSuggestion> & suggestions)210 content::ColorChooser* ExtensionViewHost::OpenColorChooser(
211 WebContents* web_contents,
212 SkColor initial_color,
213 const std::vector<content::ColorSuggestion>& suggestions) {
214 // Similar to the file chooser below, opening a color chooser requires a
215 // visible <input> element to click on. Therefore this code only exists for
216 // extensions with a view.
217 return chrome::ShowColorChooser(web_contents, initial_color);
218 }
219
RunFileChooser(WebContents * tab,const content::FileChooserParams & params)220 void ExtensionViewHost::RunFileChooser(
221 WebContents* tab,
222 const content::FileChooserParams& params) {
223 // For security reasons opening a file picker requires a visible <input>
224 // element to click on, so this code only exists for extensions with a view.
225 FileSelectHelper::RunFileChooser(tab, params);
226 }
227
228
ResizeDueToAutoResize(WebContents * source,const gfx::Size & new_size)229 void ExtensionViewHost::ResizeDueToAutoResize(WebContents* source,
230 const gfx::Size& new_size) {
231 view_->ResizeDueToAutoResize(new_size);
232 }
233
234 // content::WebContentsObserver overrides:
235
RenderViewCreated(RenderViewHost * render_view_host)236 void ExtensionViewHost::RenderViewCreated(RenderViewHost* render_view_host) {
237 ExtensionHost::RenderViewCreated(render_view_host);
238
239 view_->RenderViewCreated();
240
241 // If the host is bound to a window, then extract its id. Extensions hosted
242 // in ExternalTabContainer objects may not have an associated window.
243 WindowController* window = GetExtensionWindowController();
244 if (window) {
245 render_view_host->Send(new ExtensionMsg_UpdateBrowserWindowId(
246 render_view_host->GetRoutingID(), window->GetWindowId()));
247 }
248 }
249
250 // web_modal::WebContentsModalDialogManagerDelegate overrides:
251
252 web_modal::WebContentsModalDialogHost*
GetWebContentsModalDialogHost()253 ExtensionViewHost::GetWebContentsModalDialogHost() {
254 return this;
255 }
256
IsWebContentsVisible(WebContents * web_contents)257 bool ExtensionViewHost::IsWebContentsVisible(WebContents* web_contents) {
258 return platform_util::IsVisible(web_contents->GetNativeView());
259 }
260
GetHostView() const261 gfx::NativeView ExtensionViewHost::GetHostView() const {
262 return view_->GetNativeView();
263 }
264
GetDialogPosition(const gfx::Size & size)265 gfx::Point ExtensionViewHost::GetDialogPosition(const gfx::Size& size) {
266 if (!GetVisibleWebContents())
267 return gfx::Point();
268 gfx::Rect bounds = GetVisibleWebContents()->GetViewBounds();
269 return gfx::Point(
270 std::max(0, (bounds.width() - size.width()) / 2),
271 std::max(0, (bounds.height() - size.height()) / 2));
272 }
273
GetMaximumDialogSize()274 gfx::Size ExtensionViewHost::GetMaximumDialogSize() {
275 if (!GetVisibleWebContents())
276 return gfx::Size();
277 return GetVisibleWebContents()->GetViewBounds().size();
278 }
279
AddObserver(web_modal::ModalDialogHostObserver * observer)280 void ExtensionViewHost::AddObserver(
281 web_modal::ModalDialogHostObserver* observer) {
282 }
283
RemoveObserver(web_modal::ModalDialogHostObserver * observer)284 void ExtensionViewHost::RemoveObserver(
285 web_modal::ModalDialogHostObserver* observer) {
286 }
287
GetExtensionWindowController() const288 WindowController* ExtensionViewHost::GetExtensionWindowController() const {
289 Browser* browser = view_->GetBrowser();
290 return browser ? browser->extension_window_controller() : NULL;
291 }
292
GetAssociatedWebContents() const293 WebContents* ExtensionViewHost::GetAssociatedWebContents() const {
294 return associated_web_contents_;
295 }
296
GetVisibleWebContents() const297 WebContents* ExtensionViewHost::GetVisibleWebContents() const {
298 if (associated_web_contents_)
299 return associated_web_contents_;
300 if (extension_host_type() == VIEW_TYPE_EXTENSION_POPUP)
301 return host_contents();
302 return NULL;
303 }
304
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)305 void ExtensionViewHost::Observe(int type,
306 const content::NotificationSource& source,
307 const content::NotificationDetails& details) {
308 if (type == extensions::NOTIFICATION_EXTENSION_BACKGROUND_PAGE_READY) {
309 DCHECK(ExtensionSystem::Get(browser_context())->
310 runtime_data()->IsBackgroundPageReady(extension()));
311 LoadInitialURL();
312 return;
313 }
314 ExtensionHost::Observe(type, source, details);
315 }
316
InsertInfobarCSS()317 void ExtensionViewHost::InsertInfobarCSS() {
318 static const base::StringPiece css(
319 ResourceBundle::GetSharedInstance().GetRawDataResource(
320 IDR_EXTENSIONS_INFOBAR_CSS));
321
322 host_contents()->InsertCSS(css.as_string());
323 }
324
325 } // namespace extensions
326