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