Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_view_host.cc
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/window_controller.h"
10 #include "chrome/browser/file_select_helper.h"
11 #include "chrome/browser/platform_util.h"
12 #include "chrome/browser/ui/browser.h"
13 #include "chrome/browser/ui/browser_dialogs.h"
14 #include "chrome/common/extensions/extension_messages.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 "content/public/browser/web_contents_view.h"
20 #include "extensions/browser/extension_system.h"
21 #include "extensions/browser/runtime_data.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:
39   AssociatedWebContentsObserver(ExtensionViewHost* host,
40                                 WebContents* web_contents)
41       : WebContentsObserver(web_contents), host_(host) {}
42   virtual ~AssociatedWebContentsObserver() {}
43
44   // content::WebContentsObserver:
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
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
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
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
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
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
127 void ExtensionViewHost::OnDidStopLoading() {
128   DCHECK(did_stop_loading());
129 #if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX)
130   view_->DidStopLoading();
131 #endif
132 }
133
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
141 void ExtensionViewHost::LoadInitialURL() {
142   if (!ExtensionSystem::Get(browser_context())->
143           runtime_data()->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
161 bool ExtensionViewHost::IsBackgroundPage() const {
162   DCHECK(view_);
163   return false;
164 }
165
166 // content::WebContentsDelegate overrides:
167
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
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
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
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
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
243 void ExtensionViewHost::ResizeDueToAutoResize(WebContents* source,
244                                           const gfx::Size& new_size) {
245   view_->ResizeDueToAutoResize(new_size);
246 }
247
248 // content::WebContentsObserver overrides:
249
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*
267 ExtensionViewHost::GetWebContentsModalDialogHost() {
268   return this;
269 }
270
271 bool ExtensionViewHost::IsWebContentsVisible(WebContents* web_contents) {
272   return platform_util::IsVisible(web_contents->GetView()->GetNativeView());
273 }
274
275 gfx::NativeView ExtensionViewHost::GetHostView() const {
276   return view_->native_view();
277 }
278
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
288 gfx::Size ExtensionViewHost::GetMaximumDialogSize() {
289   if (!GetVisibleWebContents())
290     return gfx::Size();
291   return GetVisibleWebContents()->GetView()->GetViewBounds().size();
292 }
293
294 void ExtensionViewHost::AddObserver(
295     web_modal::ModalDialogHostObserver* observer) {
296 }
297
298 void ExtensionViewHost::RemoveObserver(
299     web_modal::ModalDialogHostObserver* observer) {
300 }
301
302 WindowController* ExtensionViewHost::GetExtensionWindowController() const {
303   return view_->browser() ? view_->browser()->extension_window_controller()
304                           : NULL;
305 }
306
307 WebContents* ExtensionViewHost::GetAssociatedWebContents() const {
308   return associated_web_contents_;
309 }
310
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();
316   return NULL;
317 }
318
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::Get(browser_context())->
324                runtime_data()->IsBackgroundPageReady(extension()));
325     LoadInitialURL();
326     return;
327   }
328   ExtensionHost::Observe(type, source, details);
329 }
330
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