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: 40 AssociatedWebContentsObserver(ExtensionViewHost* host, 41 WebContents* web_contents) 42 : WebContentsObserver(web_contents), host_(host) {} 43 virtual ~AssociatedWebContentsObserver() {} 44 45 // content::WebContentsObserver: 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 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 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 79 void ExtensionViewHost::CreateView(Browser* browser) { 80 view_ = CreateExtensionView(this, browser); 81 view_->Init(); 82 } 83 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 95 void ExtensionViewHost::UnhandledKeyboardEvent( 96 WebContents* source, 97 const content::NativeWebKeyboardEvent& event) { 98 view_->HandleKeyboardEvent(source, event); 99 } 100 101 // ExtensionHost overrides: 102 103 void ExtensionViewHost::OnDidStopLoading() { 104 DCHECK(did_stop_loading()); 105 view_->DidStopLoading(); 106 } 107 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 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 138 bool ExtensionViewHost::IsBackgroundPage() const { 139 DCHECK(view_); 140 return false; 141 } 142 143 // content::WebContentsDelegate overrides: 144 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 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 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 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 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 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 229 void ExtensionViewHost::ResizeDueToAutoResize(WebContents* source, 230 const gfx::Size& new_size) { 231 view_->ResizeDueToAutoResize(new_size); 232 } 233 234 // content::WebContentsObserver overrides: 235 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* 253 ExtensionViewHost::GetWebContentsModalDialogHost() { 254 return this; 255 } 256 257 bool ExtensionViewHost::IsWebContentsVisible(WebContents* web_contents) { 258 return platform_util::IsVisible(web_contents->GetNativeView()); 259 } 260 261 gfx::NativeView ExtensionViewHost::GetHostView() const { 262 return view_->GetNativeView(); 263 } 264 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 274 gfx::Size ExtensionViewHost::GetMaximumDialogSize() { 275 if (!GetVisibleWebContents()) 276 return gfx::Size(); 277 return GetVisibleWebContents()->GetViewBounds().size(); 278 } 279 280 void ExtensionViewHost::AddObserver( 281 web_modal::ModalDialogHostObserver* observer) { 282 } 283 284 void ExtensionViewHost::RemoveObserver( 285 web_modal::ModalDialogHostObserver* observer) { 286 } 287 288 WindowController* ExtensionViewHost::GetExtensionWindowController() const { 289 Browser* browser = view_->GetBrowser(); 290 return browser ? browser->extension_window_controller() : NULL; 291 } 292 293 WebContents* ExtensionViewHost::GetAssociatedWebContents() const { 294 return associated_web_contents_; 295 } 296 297 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 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 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