Home | History | Annotate | Download | only in renderer_host
      1 // Copyright (c) 2012 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/renderer_host/chrome_render_view_host_observer.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/command_line.h"
     10 #include "base/strings/stringprintf.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/chrome_notification_types.h"
     13 #include "chrome/browser/extensions/extension_service.h"
     14 #include "chrome/browser/extensions/extension_system.h"
     15 #include "chrome/browser/net/predictor.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/search_engines/search_terms_data.h"
     18 #include "chrome/browser/search_engines/template_url.h"
     19 #include "chrome/browser/search_engines/template_url_service.h"
     20 #include "chrome/browser/search_engines/template_url_service_factory.h"
     21 #include "chrome/common/chrome_switches.h"
     22 #include "chrome/common/extensions/extension.h"
     23 #include "chrome/common/extensions/extension_messages.h"
     24 #include "chrome/common/extensions/manifest.h"
     25 #include "chrome/common/render_messages.h"
     26 #include "chrome/common/url_constants.h"
     27 #include "content/public/browser/child_process_security_policy.h"
     28 #include "content/public/browser/notification_service.h"
     29 #include "content/public/browser/page_navigator.h"
     30 #include "content/public/browser/render_process_host.h"
     31 #include "content/public/browser/render_view_host.h"
     32 #include "content/public/browser/site_instance.h"
     33 #include "content/public/browser/web_contents.h"
     34 #include "content/public/common/page_transition_types.h"
     35 #include "extensions/common/constants.h"
     36 #include "net/http/http_request_headers.h"
     37 #include "third_party/skia/include/core/SkBitmap.h"
     38 #include "ui/base/window_open_disposition.h"
     39 #include "ui/gfx/codec/jpeg_codec.h"
     40 
     41 #if defined(OS_WIN)
     42 #include "base/win/win_util.h"
     43 #endif  // OS_WIN
     44 
     45 using content::ChildProcessSecurityPolicy;
     46 using content::OpenURLParams;
     47 using content::RenderViewHost;
     48 using content::SiteInstance;
     49 using content::WebContents;
     50 using extensions::Extension;
     51 using extensions::Manifest;
     52 
     53 ChromeRenderViewHostObserver::ChromeRenderViewHostObserver(
     54     RenderViewHost* render_view_host, chrome_browser_net::Predictor* predictor)
     55     : content::RenderViewHostObserver(render_view_host),
     56       predictor_(predictor) {
     57   SiteInstance* site_instance = render_view_host->GetSiteInstance();
     58   profile_ = Profile::FromBrowserContext(
     59       site_instance->GetBrowserContext());
     60 
     61   InitRenderViewForExtensions();
     62 }
     63 
     64 ChromeRenderViewHostObserver::~ChromeRenderViewHostObserver() {
     65   if (render_view_host())
     66     RemoveRenderViewHostForExtensions(render_view_host());
     67 }
     68 
     69 void ChromeRenderViewHostObserver::RenderViewHostInitialized() {
     70   // This reinitializes some state in the case where a render process crashes
     71   // but we keep the same RenderViewHost instance.
     72   InitRenderViewForExtensions();
     73 }
     74 
     75 void ChromeRenderViewHostObserver::RenderViewHostDestroyed(
     76     RenderViewHost* rvh) {
     77   RemoveRenderViewHostForExtensions(rvh);
     78   delete this;
     79 }
     80 
     81 void ChromeRenderViewHostObserver::Navigate(const GURL& url) {
     82   if (!predictor_)
     83     return;
     84   if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeFrame) &&
     85      (url.SchemeIs(chrome::kHttpScheme) || url.SchemeIs(chrome::kHttpsScheme)))
     86     predictor_->PreconnectUrlAndSubresources(url, GURL());
     87 }
     88 
     89 bool ChromeRenderViewHostObserver::OnMessageReceived(
     90     const IPC::Message& message) {
     91   bool handled = true;
     92   IPC_BEGIN_MESSAGE_MAP(ChromeRenderViewHostObserver, message)
     93     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_FocusedNodeTouched,
     94                         OnFocusedNodeTouched)
     95     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_RequestThumbnailForContextNode_ACK,
     96                         OnRequestThumbnailForContextNodeACK)
     97     IPC_MESSAGE_UNHANDLED(handled = false)
     98   IPC_END_MESSAGE_MAP()
     99   return handled;
    100 }
    101 
    102 void ChromeRenderViewHostObserver::InitRenderViewForExtensions() {
    103   const Extension* extension = GetExtension();
    104   if (!extension)
    105     return;
    106 
    107   content::RenderProcessHost* process = render_view_host()->GetProcess();
    108 
    109   // Some extensions use chrome:// URLs.
    110   // This is a temporary solution. Replace it with access to chrome-static://
    111   // once it is implemented. See: crbug.com/226927.
    112   Manifest::Type type = extension->GetType();
    113   if (type == Manifest::TYPE_EXTENSION ||
    114       type == Manifest::TYPE_LEGACY_PACKAGED_APP ||
    115       (type == Manifest::TYPE_PLATFORM_APP &&
    116        extension->location() == Manifest::COMPONENT)) {
    117     ChildProcessSecurityPolicy::GetInstance()->GrantScheme(
    118         process->GetID(), chrome::kChromeUIScheme);
    119   }
    120 
    121   // Some extensions use file:// URLs.
    122   if (type == Manifest::TYPE_EXTENSION ||
    123       type == Manifest::TYPE_LEGACY_PACKAGED_APP) {
    124     if (extensions::ExtensionSystem::Get(profile_)->extension_service()->
    125             extension_prefs()->AllowFileAccess(extension->id())) {
    126       ChildProcessSecurityPolicy::GetInstance()->GrantScheme(
    127           process->GetID(), chrome::kFileScheme);
    128     }
    129   }
    130 
    131   switch (type) {
    132     case Manifest::TYPE_EXTENSION:
    133     case Manifest::TYPE_USER_SCRIPT:
    134     case Manifest::TYPE_HOSTED_APP:
    135     case Manifest::TYPE_LEGACY_PACKAGED_APP:
    136     case Manifest::TYPE_PLATFORM_APP:
    137       // Always send a Loaded message before ActivateExtension so that
    138       // ExtensionDispatcher knows what Extension is active, not just its ID.
    139       // This is important for classifying the Extension's JavaScript context
    140       // correctly (see ExtensionDispatcher::ClassifyJavaScriptContext).
    141       Send(new ExtensionMsg_Loaded(
    142           std::vector<ExtensionMsg_Loaded_Params>(
    143               1, ExtensionMsg_Loaded_Params(extension))));
    144       Send(new ExtensionMsg_ActivateExtension(extension->id()));
    145       break;
    146 
    147     case Manifest::TYPE_UNKNOWN:
    148     case Manifest::TYPE_THEME:
    149     case Manifest::TYPE_SHARED_MODULE:
    150       break;
    151   }
    152 }
    153 
    154 const Extension* ChromeRenderViewHostObserver::GetExtension() {
    155   // Note that due to ChromeContentBrowserClient::GetEffectiveURL(), hosted apps
    156   // (excluding bookmark apps) will have a chrome-extension:// URL for their
    157   // site, so we can ignore that wrinkle here.
    158   SiteInstance* site_instance = render_view_host()->GetSiteInstance();
    159   const GURL& site = site_instance->GetSiteURL();
    160 
    161   if (!site.SchemeIs(extensions::kExtensionScheme))
    162     return NULL;
    163 
    164   ExtensionService* service =
    165       extensions::ExtensionSystem::Get(profile_)->extension_service();
    166   if (!service)
    167     return NULL;
    168 
    169   // Reload the extension if it has crashed.
    170   // TODO(yoz): This reload doesn't happen synchronously for unpacked
    171   //            extensions. It seems to be fast enough, but there is a race.
    172   //            We should delay loading until the extension has reloaded.
    173   if (service->GetTerminatedExtension(site.host()))
    174     service->ReloadExtension(site.host());
    175 
    176   // May be null if the extension doesn't exist, for example if somebody typos
    177   // a chrome-extension:// URL.
    178   return service->extensions()->GetByID(site.host());
    179 }
    180 
    181 void ChromeRenderViewHostObserver::RemoveRenderViewHostForExtensions(
    182     RenderViewHost* rvh) {
    183   ExtensionProcessManager* process_manager =
    184       extensions::ExtensionSystem::Get(profile_)->process_manager();
    185   if (process_manager)
    186     process_manager->UnregisterRenderViewHost(rvh);
    187 }
    188 
    189 void ChromeRenderViewHostObserver::OnFocusedNodeTouched(bool editable) {
    190   if (editable) {
    191 #if defined(OS_WIN) && defined(USE_AURA)
    192     base::win::DisplayVirtualKeyboard();
    193 #endif
    194     content::NotificationService::current()->Notify(
    195         chrome::NOTIFICATION_FOCUSED_NODE_TOUCHED,
    196         content::Source<RenderViewHost>(render_view_host()),
    197         content::Details<bool>(&editable));
    198   } else {
    199 #if defined(OS_WIN) && defined(USE_AURA)
    200     base::win::DismissVirtualKeyboard();
    201 #endif
    202   }
    203 }
    204 
    205 // Handles the image thumbnail for the context node, composes a image search
    206 // request based on the received thumbnail and opens the request in a new tab.
    207 void ChromeRenderViewHostObserver::OnRequestThumbnailForContextNodeACK(
    208     const SkBitmap& bitmap) {
    209   WebContents* web_contents =
    210       WebContents::FromRenderViewHost(render_view_host());
    211   const TemplateURL* const default_provider =
    212       TemplateURLServiceFactory::GetForProfile(profile_)->
    213           GetDefaultSearchProvider();
    214   if (!web_contents || !default_provider)
    215     return;
    216 
    217   const int kDefaultQualityForImageSearch = 90;
    218   std::vector<unsigned char> data;
    219   if (!gfx::JPEGCodec::Encode(
    220       reinterpret_cast<unsigned char*>(bitmap.getAddr32(0, 0)),
    221       gfx::JPEGCodec::FORMAT_SkBitmap, bitmap.width(), bitmap.height(),
    222       static_cast<int>(bitmap.rowBytes()), kDefaultQualityForImageSearch,
    223       &data))
    224     return;
    225 
    226   TemplateURLRef::SearchTermsArgs search_args =
    227       TemplateURLRef::SearchTermsArgs(base::string16());
    228   search_args.image_thumbnail_content = std::string(data.begin(), data.end());
    229   // TODO(jnd): Add a method in WebContentsViewDelegate to get the image URL
    230   // from the ContextMenuParams which creates current context menu.
    231   search_args.image_url = GURL();
    232   TemplateURLRef::PostContent post_content;
    233   GURL result(default_provider->image_url_ref().ReplaceSearchTerms(
    234       search_args, &post_content));
    235   if (!result.is_valid())
    236     return;
    237 
    238   OpenURLParams open_url_params(result, content::Referrer(), NEW_FOREGROUND_TAB,
    239                                 content::PAGE_TRANSITION_LINK, false);
    240   const std::string& content_type = post_content.first;
    241   std::string* post_data = &post_content.second;
    242   if (!post_data->empty()) {
    243     DCHECK(!content_type.empty());
    244     open_url_params.uses_post = true;
    245     open_url_params.browser_initiated_post_data =
    246         base::RefCountedString::TakeString(post_data);
    247     open_url_params.extra_headers += base::StringPrintf(
    248         "%s: %s\r\n", net::HttpRequestHeaders::kContentType,
    249         content_type.c_str());
    250   }
    251   web_contents->OpenURL(open_url_params);
    252 }
    253