Home | History | Annotate | Download | only in extensions
      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